Ownership, References, and Borrowing

https://doc.rust-lang.org/book/print.html#ownership-and-functions

  • Ownership rules

    • Each value on the heap has a (variable) owner.
    • Only one owner at a given time.
    • The value is dropped (collected?) when the owner goes out of scope.
  • Here, s2 is taking ownership of the String that s1 points to, so s1 can no longer be used.

    let s1 = String::from("hello");
    let s2 = s1;
    // Compile-time error if s1 is used here.
    
  • Here we’re only dealing with data on the stack, so no ownership rules apply:

    let x = 5; 
    let y = x;
    
  • Ownership is transferred during function calls too:

    fn main() {
      let s = String::from("hello");
    
      // s is no longer valid after this line; `takes_ownership` owns it now
      takes_ownership(s);
    }
    
  • Ownership can be transferred back to the caller via return value(s):

    fn main() {
      let s = String::from("hello");
    
      // s is no longer valid here, but s2 is.
      let s2 = takes_and_returns_ownership(s);
    }
    
  • This is too cumbersome to do everytime you want to use a value without necessarily owning it. What if we want to let a function use a value but not take ownership?

  • Rust fixes this with references.

    fn main() {
      let s1 = String::from("hello");
    
      // calculate_length gets a reference to s1, and so doesn't own s1.
      let len = calculate_length(&s1);
      println!("The length of '{}' is {}.", s1, len);
    }
    
    fn calculate_length(s: &String) -> usize { 
      s.len()
    }
    
  • Having references as function parameters is called borrowing.

  • A function that accepts a reference can’t modify the data it refers to, unless it’s a mutable reference:

    fn main() {
      let mut s = String::from("hello");
      change(&mut s);
    }
    
    fn change(some_string: &mut String) {
      some_string.push_str(", world");
    }
    
  • There are rules around mutable references, though, which allows Rust to avoid data races at compile time:

    • You can have only one mutable reference to a particular piece of data in a particular scope.
    • You cannot have a mutable reference while you have an immutable one (in a particular scope).
    • Effectively, this is “either one mutable reference or many immutable references are allowed in a given scope”.
  • A reference’s scope starts from where it is introduced and continues through the last time that reference is used, so scopes are not necessarily denoted by {}.

    fn main() {
      let mut s = String::from("hello");
    
      let r1 = &s;
      let r2 = &s;
      println!("{} and {}", r1, r2);
      // r1 and r2's scope _implicitly_ ends here
    
      // so this is fine
      let r3 = &mut s;
      println!("{}", r3);
    }
    
Edit