« Back to Index

Rust: Ownership, Borrowing, Lifetimes

View original Gist on GitHub

Tags: #rust #rustlang #ownership #memory

Rust Ownership, Borrowing, Lifetimes.md

Ownership

NOTE: all information learned from https://doc.rust-lang.org/stable/book/ch04-00-understanding-ownership.html

Understanding ownership requires understanding ‘stack’ vs ‘heap’ memory.

Rules

Gotchas

Borrowing

NOTE: all information learned from https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html

Taking ownership and then returning ownership with every function is a bit tedious. To prevent this you can pass a ‘reference’ to a complex type (e.g. function foo(s: &String) and caller foo(&a)).

In the above example the s variable will (depending on function implementation) go out of scope, and yet nothing will happen (i.e. it won’t be dropped) because the function doesn’t own what s refers to.

In order to mutate something borrowed, the caller and the receiver need to define the type as a ‘mutable’ type:

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

    change(&mut s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

Gotchas

Lifetimes

NOTE: all information learned from https://doc.rust-lang.org/stable/book/ch10-03-lifetime-syntax.html

Lifetimes ultimately are coupled to references, hence the compiler uses what’s called a “borrow checker” to validate lifetimes (as a ‘reference’ is a term related to the concept of “borrowing”).

Rust prevents variables from trying to hold references to data that has since gone out of scope (i.e. dangling pointer).

The ‘lifetime’ of a reference begins when the reference is created and ends when it’s last used.

If a function returns a reference that changes depending on some logic (e.g. if X return A else return B, where A/B are two different references) then the borrow checker can’t statically analyse if your code is safe as it doesn’t know which reference will be returned at runtime.

In those cases we need to add lifetime annotations…

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

The longest function definition states all references in the signature must have the same lifetime 'a.

We’re specifying that the borrow checker should reject any values that don’t adhere to these constraints.

The lifetime named ‘static’ is a special lifetime. It signals that something has the lifetime of the entire program.

String literals can be assigned the type &'static lifetime annotation as a way to indicate the reference is always alive, i.e. they are baked into the data segment of the final binary.