References and Borrowing

How to use a value without taking ownership of it. Shared and mutable references, the borrowing rules, and the errors you hit when you break them.

Moving a value into a function every time you want to read it gets old fast. You pass a String to calculate_length, and now you can't use it anymore unless the function hands it back. References fix this. A reference lets a function borrow a value: it can use it, but the caller still owns it.

The problem references solve

fn calculate_length(s: String) -> usize {
    s.len()
} // s is dropped here

fn main() {
    let name = String::from("ferris");
    let len = calculate_length(name);
    // println!("{name}"); // error: name was moved
    println!("{len}");
}

name moved into the function and was dropped at the end of it. To keep using it, you would have to return it as part of a tuple. That works, but it's noise.

Shared references: &T

Put & in front of the value and in front of the parameter type:

fn calculate_length(s: &String) -> usize {
    s.len()
} // s goes out of scope, but it doesn't own the String, so nothing is dropped

fn main() {
    let name = String::from("ferris");
    let len = calculate_length(&name);
    println!("{name} has {len} letters");
}

&name creates a reference that points at name without owning it. Creating a reference is called borrowing. When the function returns, the borrow ends and name is still fully usable.

A shared reference is read-only. This does not compile:

fn add_suffix(s: &String) {
    s.push_str("!"); // error[E0596]: cannot borrow `*s` as mutable
}

Prefer &str over &String:

For function parameters, &str is usually better than &String. A &String coerces to &str automatically, and &str also accepts string literals and slices. The same goes for &[T] instead of &Vec<T>.

Mutable references: &mut T

If the function needs to change the value, borrow it mutably. The variable itself must be declared mut:

fn add_suffix(s: &mut String) {
    s.push_str("!");
}

fn main() {
    let mut greeting = String::from("hello");
    add_suffix(&mut greeting);
    println!("{greeting}"); // hello!
}

The two borrowing rules

At any point in time you can have either:

  1. any number of shared references (&T), or
  2. exactly one mutable reference (&mut T).

Not both. And every reference must always point to a valid value.

That's the whole rule set. It's what stops data races at compile time: two pieces of code can't change the same data at once, and nobody can read data while someone else is changing it.

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

    let r1 = &mut s;
    let r2 = &mut s; // error[E0499]: cannot borrow `s` as mutable more than once at a time
    println!("{r1}, {r2}");
}

Mixing shared and mutable borrows fails the same way:

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

    let r1 = &s;
    let r2 = &mut s; // error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
    println!("{r1}, {r2}");
}

Borrows end at their last use

A borrow doesn't last until the end of the block. It lasts until the reference is used for the last time. This is called non-lexical lifetimes (NLL), and it makes most real code just work:

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

    let r1 = &s;
    let r2 = &s;
    println!("{r1} and {r2}"); // last use of r1 and r2

    let r3 = &mut s; // fine: the shared borrows are already over
    r3.push_str(" world");
    println!("{r3}");
}

If you hit E0502, look for a shared reference that is still used after the mutable borrow. Moving that use earlier, or cloning the small piece you need, usually fixes it.

Dangling references are not allowed

A reference can't outlive the value it points to. Returning a reference to a local variable fails:

fn dangle() -> &String { // error[E0106]: missing lifetime specifier
    let s = String::from("hello");
    &s
} // s is dropped here, so the reference would point at freed memory

The fix is to return the owned value instead:

fn no_dangle() -> String {
    String::from("hello")
}

Quick reference

You want toUseCaller writes
Read a value without taking it&Tf(&x)
Change a value without taking it&mut Tf(&mut x) (and let mut x)
Take the value for goodTf(x)

Common errors

  • E0382 borrow of moved value: you passed x instead of &x.
  • E0596 cannot borrow as mutable: the variable isn't mut, or the parameter is &T instead of &mut T.
  • E0499 / E0502: two borrows overlap. Shorten one of them.
  • E0106 missing lifetime specifier: you're returning a reference to something local. Return an owned value.

Next, The Slice Type shows a special kind of reference that points at part of a collection.