References and Borrowing

Immutable References

An immutable reference (also called a shared reference), written &T, is a read-only pointer that borrows a value of type T without taking ownership of it. Multiple immutable references to the same value can exist and be used simultaneously, provided no mutable reference (&mut T) to that value is active at the same time.

Creating and Using an Immutable Reference

Apply the & operator to an expression to create an immutable reference &T:

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

fn main() {
    let document = String::from("Immutable references allow shared read access.");

    // Create two immutable references borrowing `document` at the same time:
    let reader_a: &String = &document;
    let reader_b: &String = &document;

    println!("Word count via reader_a: {}", count_words(reader_a));
    println!("Direct read via reader_b: {reader_b}");
    println!("Original owner still intact: {document}");
}
Word count via reader_a: 6
Direct read via reader_b: Immutable references allow shared read access.
Original owner still intact: Immutable references allow shared read access.

Why &T Is Called a "Shared Reference"

In Rust documentation and compiler messages, &T is referred to as both an immutable reference and a shared reference:

  • Immutable: You cannot modify the target value through &T (*r = new_val is forbidden). Furthermore, as long as any &T reference is alive, even a let mut owner cannot modify or move the value!
  • Shared: Unlike &mut T (which is exclusive and cannot be copied), a shared reference &T implements the Copy trait—for every type T, even when T itself is not Copy (such as &String or &Vec<u8>)!
Because `&T` implements `Copy` for ALL `T`:
  let s = String::from("heap");   // `String` is NOT Copy
  let r1: &String = &s;           // `&String` IS Copy! (8-byte pointer)
  let r2: &String = r1;           // Copies the 8-byte pointer; both `r1` and `r2` remain valid!
fn main() {
    let owned_vec = vec![10, 20, 30];

    let r1: &Vec<i32> = &owned_vec;
    let r2: &Vec<i32> = r1; // Copies the 8-byte reference `r1` without moving it!

    println!("r1 = {r1:?}, r2 = {r2:?}");
}
r1 = [10, 20, 30], r2 = [10, 20, 30]

Taking &T of an Immutable vs Mutable Variable

You can create an immutable reference &T from either an immutable variable (let x) or a mutable variable (let mut y). When you borrow a mutable variable y as &y, y is temporarily frozen (read-only) for the duration of the immutable borrow:

fn main() {
    let mut counter = 10;

    // Borrow `counter` immutably:
    let snap: &i32 = &counter;
    println!("Snapshot while frozen: {snap}");
    // `snap` is not used after this line, so the immutable borrow ends here!

    // Now `counter` can be mutated again:
    counter += 5;
    println!("Updated counter: {counter}");
}
Snapshot while frozen: 10
Updated counter: 15

Common Compiler Errors with &T

1. Attempting to Mutate Through an Immutable Reference (E0594 / E0596)

Calling a mutating method (.push(), .clear()) or assigning *r = ... through &T is rejected at compile time:

// ❌ does not compile
fn try_append(list: &Vec<i32>) {
    list.push(4);
}

fn main() {
    let mut v = vec![1, 2, 3];
    try_append(&v);
}
error[E0596]: cannot borrow `*list` as mutable, as it is behind a `&` reference
 --> src/main.rs:2:5
  |
2 |     list.push(4);
  |     ^^^^ `list` is a `&` reference, so the data it refers to cannot be borrowed as mutable
  |
help: consider changing this to be a mutable reference
  |
1 | fn try_append(list: &mut Vec<i32>) {
  |                      +++

error: aborting due to 1 previous error

2. Mutating or Moving the Owner While an Immutable Borrow Is Active (E0502 / E0505)

As long as an immutable reference r is still going to be used on a later line, the owner variable cannot be mutated (E0502) or moved out of (E0505):

// ❌ does not compile
fn main() {
    let mut name = String::from("Ferris");
    let borrowed: &String = &name;

    name = String::from("Replaced"); // Error: `name` is currently borrowed by `borrowed`!

    println!("{borrowed}");
}
error[E0506]: cannot assign to `name` because it is borrowed
 --> src/main.rs:5:5
  |
3 |     let borrowed: &String = &name;
  |                             ----- `name` is borrowed here
4 |
5 |     name = String::from("Replaced");
  |     ^^^^ `name` is assigned to here but it was already borrowed
6 |
7 |     println!("{borrowed}");
  |               ---------- borrow later used here

error: aborting due to 1 previous error