Borrowing and References

Borrowing and References

In rust every value has an owner, at a given point in time there can be only one owner to the value. The value will be dropped once the owner goes out of the scope. Borrowing is something that we don't own but we can use it to perform some operation and give it back. A reference is like a pointer in that it’s an address we can follow to access the data stored at that address; that data is owned by some other variable. Unlike a pointer, a reference is guaranteed to point to a valid value of a particular type for the life of that reference.

Rules of References

  • In rust at any given point in time either you can have one mutable reference or any number of immutable references.
  • References must always be valid.

Consider the below example

fn main() {
    let the_missing_semicolon = String::from("Hello world");
    let length_of_string = calcluate_length_of_string(the_missing_semicolon);

    println!("{}:{}", the_missing_semicolon, length_of_string)
}

fn calcluate_length_of_string(string: String) -> usize {
    string.len()
}

When we run the above code we get the following error:

error[E0382]: borrow of moved value: `the_missing_semicolon`
 --> src/main.rs:7:23
  |
4 |     let the_missing_semicolon = String::from("Hello world");
  |         --------------------- move occurs because `the_missing_semicolon` has type `String`, which does not implement the `Copy` trait
5 |     let length_of_string = calcluate_length_of_string(the_missing_semicolon);
  |                                                       --------------------- value moved here
6 |
7 |     println!("{}:{}", the_missing_semicolon, length_of_string)
  |                       ^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move
  |
  = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0382`.
warning: `refernces_and_borrowing` (bin "refernces_and_borrowing") generated 1 warning
error: could not compile `refernces_and_borrowing` due to previous error; 1 warning emitted

Explanation

When we pass the string to the calcluate_length_of_string function, ownership of the value is transferred to the function it is being passed. To mitigate this kind of error either we can create a copy of the variable and pass it to the function or we can simply make the function to accept the references.

Reference

fn main() {
    let the_missing_semicolon = String::from("Hello world");
    let length_of_string = calcluate_length_of_string(&the_missing_semicolon);

    println!(
        "The string is {}: The length of the string is {}",
        the_missing_semicolon, length_of_string
    )
}

fn calcluate_length_of_string(string: &String) -> usize {
    string.len()
}

Explanation

In the above code snippet, we are passing a reference to the function, so that function does not own the data. Even though the reference is used, the value will not be dropped as it is not owned by this function.

The string is Hello world: the length of the string is 11

Mutating Borrowed Values

We can mutate borrowed value by mutable reference.

Example


fn main() {
    let mut the_missing_semicolon = String::from("Hello world");

    println!("String is {}:", the_missing_semicolon);

    mutate(&mut the_missing_semicolon);

    println!("Updated String value is {}:", the_missing_semicolon);
}

fn mutate(string: &mut String) {
    string.push_str(" mutated")
}

Output

String is Hello world
Updated String value is Hello world mutated

There is a major restriction for mutability, if you have a mutable reference to a value then you can't have another mutable reference to the same value. i.e

fn main() {
    let mut the_missing_semicolon = String::from("Hello world");

    let new_string = &mut the_missing_semicolon;
    let new_string_2 = &mut the_missing_semicolon;

    println!("{}, {}", new_string, new_string_2);
}

The above code fails due to restriction for mutability as multiple mutable borrows are made to the value. The compiler will throw the below error.

error[E0499]: cannot borrow `the_missing_semicolon` as mutable more than once at a time
 --> src/main.rs:5:24
  |
4 |     let new_string = &mut the_missing_semicolon;
  |                      -------------------------- first mutable borrow occurs here
5 |     let new_string_2 = &mut the_missing_semicolon;
  |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
6 |
7 |     println!("{}, {}", new_string, new_string_2);
  |                        ---------- first borrow later used here

We can have any number of immutable references or only one mutable reference.

fn main() {
    let mut the_missing_semicolon = String::from("Hello world");

    let new_string_2 = &the_missing_semicolon;
    println!("{}", new_string_2);

    let new_string = &mut the_missing_semicolon;
    new_string.push_str(" Mutated");

    println!("{}", new_string);

    let new_string_4 = &the_missing_semicolon;
    let new_string_5 = &the_missing_semicolon;

    println!("{} , {}", new_string_4, new_string_5);
}
Hello world
Hello world Mutated
Hello world Mutated, Hello world Mutated

We also cannot have a mutable reference when we have an immutable one with the same value.

 let mut s = String::from("hello");

    let r1 = &s; 
    let r2 = &s; 
    let r3 = &mut s; // this will throw error

    println!("{}, {}, and {}", r1, r2, r3);

The above code complies with the below error:

error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
 --> src/main.rs:6:14
  |
4 |     let r1 = &s; 
  |              -- immutable borrow occurs here
5 |     let r2 = &s; 
6 |     let r3 = &mut s; // this will throw error
  |              ^^^^^^ mutable borrow occurs here
7 |
8 |     println!("{}, {}, and {}", r1, r2, r3);
  |                                -- immutable borrow later used here

Users of an immutable reference don’t expect the value to suddenly change out from under them! However, multiple immutable references are allowed because no one who is just reading the data can affect anyone else’s reading of the data.

Note: In rust the compiler guarantees that references will never be dangling references: if you have a reference to some data, the compiler will ensure that the data will not go out of scope before the reference to the data does.

Did you find this article valuable?

Support Shreyas K S by becoming a sponsor. Any amount is appreciated!