Rust Ownership and Borrowing

saiumesh

sai umesh

Posted on July 18, 2019

Rust Ownership and Borrowing

Before we talk about Rust Ownership and Borrowing, I would like you to know that techempower has released round 18 results last week and Rust has clearly dominated in 4 of 5 categories, which shows how powerful Rust has become over the years.

Rust is a very powerful programming language but, learning Rust is not easy. it has a steep learning curve. The first thing you would find difficult to learn in Rust is probably going to be Ownership and Borrowing. Today we will try to learn basics of it.

This article is aimed at learning basics of it so, use this code in production with caution.

Rust neither has manual memory management nor garbage collector. Then the next obvious question would be, how does Rust manage memory 💭💭. Here comes Rust's most unique point, Ownership and Borrowing. let's see how does it work with some examples.

before we see any black and blue text, we need know about two types of variables:

  1. Static allocated types ex: int
  2. Heap Allocated types ex: array(Vector in Rust)

For any statically allocated types, Rust Ownership and Borrowing doesn't apply but for heap allocation it does apply. let's checkout some examples.


fn do_something(input: i32) -> i32 {
    input * 2
}


fn main() {
    let number: i32 = 1;
    let result = do_something(number);
    println!("result is {} for number {}", result, number)
}

run it on playground

if you run above program you would get following output, as expected.

result is 2 for number 1

now let's replace i32 (statically allocated) with String (Heap allocation) in above example.

fn do_something(input: String) -> String {
    // do something with string
    input
}


fn main() {
    let name: String = String::from("Hello!!");
    let result = do_something(name);
    println!("result is {} for string {}", result, name)
}

run it on playground

once you run above program you will encounter first O & B problem as follows.

--> src\main.rs:11:52
   |
9  |     let name: String = String::from("Hello!!");
   |         ---- move occurs because `name` has type `std::string::String`, which does not implement the `Copy` trait
10 |     let result = do_something(name);
   |                               ---- value moved here
11 |     println!("result is {} for string {}", result, name)
   |                                                    ^^^^ value borrowed here after move

Any heap allocated variable will have it's scope, and generally the scope is of lifetime of it's function(ownsership). but, if you pass any heap variable to another function, not only the variable but also it's scope will be passed to that function(ownsership) along with it's lifetime. this means, main() will not have access to name of type String once it's passed down to do_something(). once do_something() is done with it's execution, input variable of type String memory will be dropped. This is how Rust maintains memory management without managing manually.

Now, To solve above problem, we will send reference of the string but not actual variable.

fn do_something(input: &String) -> String {
    // do something with string
    input.to_string()
}


fn main() {
    let name: String = String::from("Hello!!");
    let result = do_something(&name);
    println!("result is {} for string {}", result, name)
}

run it on playground

if you run above program you would get following output.

result is Hello!! for name Hello!!

any variable in rust is immutable unless say otherwise. now let's try to mutate input and let's see what happens.


fn do_something(input: &String) {
    input.push_str("world!!")
}


fn main() {
    let name: String = String::from("Hello!!");
    do_something(&name);
    println!("result is {} for name", name)
}

run on playground

if you run above program you would get following output.

error[E0596]: cannot borrow `*input` as mutable, as it is behind a `&` reference
 --> src\main.rs:3:5
  |
2 | fn do_something(input: &String) {
  |                        ------- help: consider changing this to be a mutable reference: `&mut std::string::String`
3 |     input.push_str("World!!")
  |     ^^^^^ `input` is a `&` reference, so the data it refers to cannot be borrowed as mutable

since borrowed variable not mutable, we cannot mutate it. to solve the problem we need to make both original and passed variable mut mutable.

fn do_something(input: &mut String) {
    input.push_str(" World!!")
}


fn main() {
    let mut name: String = String::from("Hello!!");
    do_something(&mut name);
    println!("result is {} for name", name)
}

run it on playground

if you run above program you would get following output.

result is Hello!! World!! for name

I hope you enjoyed this article. If you have any doubt or help, you can contact me on twitter.

In next article we will talk about, how to create sharable variables between the threads.

Thanks for reading. Have a Good day!

💖 💪 🙅 🚩
saiumesh
sai umesh

Posted on July 18, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

December Surely Looks Busy!
opensource December Surely Looks Busy!

November 29, 2024

December Surely Looks Busy!
opensource December Surely Looks Busy!

November 29, 2024

Daemons on macOS with Rust
undefined Daemons on macOS with Rust

November 29, 2024