Ownership and Borrowing

Ownership

Rust by example - Ownership

  • No garbage collector. Ownership instead.
  • Each value in Rust has an owner.
  • There can only be one owner at a time.
/// This example demonstrates the concept of ownership and move semantics in
/// Rust.
fn main() {
    // Strings have move semantics.
    // `s1` owns the string "hello".
    let s1 = String::from("hello");
    // `s1` is MOVED into `s2`.
    // This is NOT a shallow copy.
    // `s2` now owns the string "hello".
    let s2 = s1;
    println!("{}, world!", s2);
    // Rust has invalidated `s1` because the String has been moved to `s2`.
    // ERROR: println!("{}, world!", s1);
}

When the owner goes out of scope, the value will be dropped.

fn main() {
    // `s` is not valid here, it’s not yet declared.
    {
        // `s` is valid from this point forward.
        let s = String::from("hello");
        println!("{}", s);
    } // this scope is now over, and `s` is no longer valid.
    // Rust calls `drop`.

    // ERROR println!("{}", s); // `s` is not valid here, it’s no longer in
    // scope.
}

Rust will never automatically create deep copies of your data. Use std::clone::Clone⮳.

/// This example demonstrates the use of the `clone` method to create a deep
/// copy of a `String`.
fn main() {
    // Create a String on the heap.
    let s1 = String::from("hello");
    // Clone the String, creating a new String with the same content on the
    // heap.
    let s2 = s1.clone();
    // `clone` deeply copies the heap data of the `String`,
    // not just the stack data.
    println!("{s2}");
}

If a type implements the std::marker::Copy⮳ trait (stack-only, fixed-size values, like integers, floats, and tuples thereof), variables that use it do not move, but rather are trivially copied, making them still valid after assignment to another variable.

/// Integers implement the `Copy` trait, so they are copied instead of moved.
fn main() {
    let x = 5; // x is an integer.
    let y = x; // y is a copy of x.

    // Both x and y are valid here.
    println!("x = {}, y = {}", x, y);
}

Borrowing

Passing a variable to a function will move or copy, just as assignment does. To avoid passing a value along, borrow the value:

//! Demonstrates the concept of borrowing in Rust.
fn main() {
    let s1 = String::from("hello");

    let _len = calculate_length(&s1); // `&s1` passes an immutable reference to `s1`

    fn calculate_length(s: &str) -> usize {
        s.len()
    } // `s` goes out of scope here. Because the function does not have
    // ownership of what it refers to, `s1` is not dropped.

    println!("{s1}");
}

Mutable References

/// This function takes a mutable reference to a String and appends ", world" to
/// it.
fn change(some_string: &mut String) {
    some_string.push_str(", world"); // Modifies the string in place.
    println!("{some_string}");
}

fn main() {
    let mut s = String::from("hello"); // Note the `mut` keyword.
    change(&mut s);
}

If you have a mutable reference to a value, you can have no other simultaneous references to that value! Functions like a read/write lock.

Related Topics

  • Lifetimes.
  • Rust Patterns.

References