RustArena

Borrowing and references in Rust (&T and &mut T)

How borrowing works in Rust: shared references &T, mutable references &mut T, the one writer or many readers rule, and dereferencing with *.

Moving a value into every function that needs to look at it would be painful. Borrowing lets code use a value without taking ownership: you hand out a reference, and the owner keeps the value.

fn count_words(text: &str) -> usize {
    text.split_whitespace().count()
}

fn add_signature(text: &mut String) {
    text.push_str(" -- Ferris");
}

fn main() {
    let mut note = String::from("ship it on friday");
    println!("{} words", count_words(&note));
    add_signature(&mut note);
    println!("{note}");
}
4 words
ship it on friday -- Ferris

note is never moved, so main can keep using it.

The borrowing rules

At any moment, a value can have either:

but not both. And a reference can never outlive the value it points to.

This "many readers or one writer" rule is checked at compile time by the borrow checker. It's what prevents a whole family of bugs:

Borrows end at their last use

A borrow lasts from where it's created to the last place it's used, not to the end of the block (this is called non-lexical lifetimes). This compiles, because first isn't used after the push:

fn main() {
    let mut scores = vec![10, 20];
    let first = &scores[0];
    println!("first {first}");
    scores.push(30);
    println!("{scores:?}");
}
first 10
[10, 20, 30]

Move the println! of first below the push and you get error E0502.

Dereferencing with *

To read or change the value behind a reference, use *: *count += 1. Method calls and field access dereference automatically (text.len(), player.score), and so does println!, so you mostly need * for assignment and arithmetic on numbers.

The errors you'll meet

Error Meaning
E0502 mutable borrow while a shared borrow is still in use (or the reverse)
E0499 two mutable borrows alive at once
E0596 &mut of something not declared mut, or behind a &
E0597 the value is dropped while a reference to it is still used
E0506 assigning to a value while it's borrowed
E0515 returning a reference to a local variable

Each one is the compiler finding a real bug pattern, not being pedantic.

Best practices

Example

fn total(prices: &[u32]) -> u32 {
    let mut sum = 0;
    for p in prices {
        sum += p;
    }
    sum
}

fn apply_discount(prices: &mut [u32], percent: u32) {
    for p in prices.iter_mut() {
        *p -= *p * percent / 100;
    }
}

fn bump(counter: &mut u32) {
    *counter += 1;
}

fn main() {
    let mut cart = vec![100, 250, 40];
    let before = total(&cart);
    apply_discount(&mut cart, 10);
    let after = total(&cart);
    println!("{cart:?}");
    println!("before {before}, after {after}");

    let mut visits = 0;
    bump(&mut visits);
    bump(&mut visits);
    println!("visits {visits}");

    let r1 = &cart;
    let r2 = &cart;
    println!("two readers: {} {}", r1.len(), r2[0]);
}

Output:

[90, 225, 36]
before 390, after 351
visits 2
two readers: 3 90

Practice it