OWNERSHIP, REFERENCES AND BORROWING

                                 OWNERSHIP

  •  Ownership is a set of rules that rust uses to manage memomory.
  • These rules allow you to write clean and efficient codes which are not susceptible to racing, deadlock etc..
  • If even a single one of these rules is not followed when you write your code, your code compile.
  • let s1: String = String::from("hello");
    let s2: String = s1;
  • The above piece of code will throw an error during compile time:
  • borrow of moved value: `s1`
    let s1 = String::from("hello");
        -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
    let s2 = s1;
             -- value moved here
    println!("My favorite fruit is {}!", s1);
                                         ^^ value borrowed here after move
    
  • The above error occurs due to the the value of s1 being moved to s2 and s1 being empty.
  • The data type String has three pieces of data: Pointer, Length and capacity which is stored in the stack where the pointer points to the string in the heap, and length is the length of the String (to know where excatly the string ends) and the capacity is the limit of the string.
                                                
  • The the box in the left shows the string data stored in stack and the box in the right side shows the actual string data stored in heap.
  • let s1: String = String::from("hello");
    let s2: String = s1;
  • Now look at the above piece of code, whose representation is shown in the below image.
                                         
  • The above image shows that the variable s1 and s1 points to the same string in heap, which is not a good thing as the rust compiler tries to dellocate the string stored in the heap two times after the execution of that block of code, which will result in errors.
  • To overcome this issue rust makes use the concept of moving (transferring ownership). Which moves all the data in s1 to s2, thus making s1 useless.
  • Moving is done via triats called copy.
  • So how to make a copy of s1 into s2?
  • let s1: String = String::from("hello");
    let s2: String = s1.clone()
  • The above code makes a deep copy of s1 into s2, essentially, the same as given below image.
  • This kind of error doesn't happen for integer and float data types as they are directly stored in stack and when assigning to another variable, we make a copy of it in stack (no moving).

                   REFERENCES AND BORROWING

  • Suppose that you need to use the value of a string for a function, then how to do it?
  • It can by done using reference, i.e, it provides just read-only access.
  • It can be done by putting a & before the variable as shown in the below code:
  • let s1: String = String::from("hello");
    let s2: String = &s1;

  • The above image shows what reference does.
  • It stores the pointer to the stack which contains the data(s1) of the string stored in heap.
  • It doesn't point directly to the string in heap.
  • Reference is stored in stack.
  • The reference gets removed when the code gets out of the scope.
  • You can use reference in function like below:
  •   fn greet(a: &String){
    
    println!("{}", a);
    } fn main(){
    let s1: String = String::from("hello");
    greet(&S1)
    }

  • If we try to make changes in the referenced value, it will throw an error as given below.
  • $ cargo run
       Compiling ownership v0.1.0 (file:///projects/ownership)
    error[E0596]: cannot borrow `*some_string` as mutable, as it is behind a `&` reference
     --> src/main.rs:8:5
      |
    7 | fn change(some_string: &String) {
      |                        ------- help: consider changing this to be a mutable reference: `&mut String`
    8 |     some_string.push_str(", world");
      |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `some_string` is a `&` reference, so the data it refers to cannot be borrowed as mutable
    
    For more information about this error, try `rustc --explain E0596`.
    error: could not compile `ownership` due to previous error
    

MUTABLE REFERENCE

  • Borrowed Reference can be made mutable by adding the key mut as shown in the given below code.
  • fn main() {
        let mut s = String::from("hello");
    
        change(&mut s);
    }
    
    fn change(some_string: &mut String) {
        some_string.push_str(", world");
    }
    
  • But mutable reference has a big disadvantage, if you have it, you can have no other reference as show below:
  • fn main() {
        let mut s = String::from("hello");
    
        let r1 = &mut s;
        let r2 = &mut s;
    
        println!("{}, {}", r1, r2);
    }
    
  • And it will lead to an error as shown below:
  • $ cargo run
       Compiling ownership v0.1.0 (file:///projects/ownership)
    error[E0499]: cannot borrow `s` as mutable more than once at a time
     --> src/main.rs:5:14
      |
    4 |     let r1 = &mut s;
      |              ------ first mutable borrow occurs here
    5 |     let r2 = &mut s;
      |              ^^^^^^ second mutable borrow occurs here
    6 | 
    7 |     println!("{}, {}", r1, r2);
      |                        -- first borrow later used here
    
    For more information about this error, try `rustc --explain E0499`.
    error: could not compile `ownership` due to previous error
    
  • This restriction allows us to prevent data races.
  • Data race is racing that occurs due to these behaviours:
              - Two or more pointer access the same data at the same time.
              - At least one of the pointer is being used to write in to data.
              - There is no mechanism being used to synchronize acess to data.
  • You cannot have a mutable reference when you have a immutable reference
  • fn main() {
        let mut s = String::from("hello");
    
        let r1 = &s; // no problem
        let r2 = &s; // no problem
        let r3 = &mut s; // BIG PROBLEM
    
        println!("{}, {}, and {}", r1, r2, r3);
    }
    
  • It leads to below error
  • $ cargo run
       Compiling ownership v0.1.0 (file:///projects/ownership)
    error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
     --> src/main.rs:6:14
      |
    4 |     let r1 = &s; // no problem
      |              -- immutable borrow occurs here
    5 |     let r2 = &s; // no problem
    6 |     let r3 = &mut s; // BIG PROBLEM
      |              ^^^^^^ mutable borrow occurs here
    7 | 
    8 |     println!("{}, {}, and {}", r1, r2, r3);
      |                                -- immutable borrow later used here
    
    For more information about this error, try `rustc --explain E0502`.
    error: could not compile `ownership` due to previous error
    
  • The correct way of using it is:
  • fn main() {
        let mut s = String::from("hello");
    
        let r1 = &s; // no problem
        let r2 = &s; // no problem
        println!("{} and {}", r1, r2);
        // variables r1 and r2 will not be used after this point
    
        let r3 = &mut s; // no problem
        println!("{}", r3);
    }
    
  • The scope of r1 and r2 ends before r3 is created, so no error will be detected.

DANGLING REFERENCES

  • Dangling reference is a pointer that references a location in memory that may have been given to someone else - while freeing some memory while preserving a pointer to that memory. Given below is an example of dangling reference.
  • fn main() {
        let reference_to_nothing = dangle();
    }
    
    fn dangle() -> &String {
        let s = String::from("hello");
    
        &s
    }
    
  • The above code will show below error:
  • $ cargo run
       Compiling ownership v0.1.0 (file:///projects/ownership)
    error[E0106]: missing lifetime specifier
     --> src/main.rs:5:16
      |
    5 | fn dangle() -> &String {
      |                ^ expected named lifetime parameter
      |
      = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
    help: consider using the `'static` lifetime
      |
    5 | fn dangle() -> &'static String {
      |                ~~~~~~~~
    
    For more information about this error, try `rustc --explain E0106`.
    error: could not compile `ownership` due to previous error
    
  • It can be fixed using the below format:
  • fn main() {
        let string = no_dangle();
    }
    
    fn no_dangle() -> String {
        let s = String::from("hello");
    
        s
    }
  • Point to note is that you can have any number of immutable refernce, but only one mutable reference.

Comments