References and Borrowing

Mutable References

A mutable reference (also called an exclusive reference), written &mut T, is a pointer that borrows temporary read and write access to a value of type T without taking ownership of it. To prevent data races, iterator invalidation, and aliasing bugs, Rust enforces a strict rule: at most one active &mut T reference to a given value may exist at a time, and no shared &T references may coexist with it.

Creating and Using a Mutable Reference

To create a mutable reference &mut T:

  1. The owner variable itself must be declared mutable (let mut x = ...;).
  2. Borrow the variable using &mut x.
  3. Inside the borrower, mutate the value either via method calls (such as r.push(...), which auto-dereferences r) or via the explicit dereference operator (*r = new_val):
fn normalize_and_append(buffer: &mut String, suffix: &str) {
    // 1. Mutating methods auto-dereference `buffer: &mut String`:
    *buffer = buffer.trim().to_lowercase();
    buffer.push_str(suffix);
}

fn increment_in_place(counter: &mut i32) {
    // 2. Explicit `*` dereference on primitive value:
    *counter += 1;
}

fn main() {
    let mut name = String::from("  FERRIS_THE_CRAB  ");
    let mut visits = 0;

    normalize_and_append(&mut name, "_2024");
    increment_in_place(&mut visits);
    increment_in_place(&mut visits);

    println!("name = '{name}', visits = {visits}");
}
name = 'ferris_the_crab_2024', visits = 2

Why &mut T Is Called an "Exclusive Reference"

In Rust, &mut T means more than just "this pointer can write to T." It guarantees exclusive access: while a &mut T reference is active, no other reference (&T or &mut T) and not even the original owner variable itself is allowed to read or write that value until the mutable borrow ends!

While `let r: &mut Vec<i32> = &mut v;` is active:
  +---------------+         +-------------------+
  |   v (Owner)   |         | r: &mut Vec<i32>  |
  | [Temporarily  |         | [SOLE active path |
  |  inaccessible]|         |  to read or write]|
  +---------------+         +---------+---------+
                                      │
                                      v
                            [ Heap Vector Buffer ]

Because &mut T guarantees uniqueness, &mut T does NOT implement the Copy trait! If &mut T were Copy, assigning let r2 = r1; would create two simultaneous mutable aliases to the same memory location. Instead, assigning let r2 = r1; moves (or reborrows) the mutable reference.

Sequential Mutable Borrows (One After Another)

Although you cannot hold two active &mut references to the same variable at the same time, you can create as many &mut references as you want sequentially—as long as each earlier &mut reference is no longer used before the next one is created:

fn main() {
    let mut balance = 100;

    let first_borrow: &mut i32 = &mut balance;
    *first_borrow += 50;
    println!("After first_borrow: {first_borrow}");
    // `first_borrow` is never used again below this line, so its lifetime ends here!

    let second_borrow: &mut i32 = &mut balance;
    *second_borrow -= 30;
    println!("After second_borrow: {second_borrow}");

    println!("Final balance: {balance}");
}
After first_borrow: 150
After second_borrow: 120
Final balance: 120

Swapping and Taking Values Through &mut T (std::mem)

Remember that you cannot move ownership out of *r directly (let x = *r; fails with E0507 if T is not Copy), because doing so would leave the borrowed owner's memory slot uninitialized.

However, because &mut T has exclusive write access, the std::mem module provides three safe, zero-allocation functions that extract an owned value from &mut T while immediately putting a valid replacement value in its place:

Function Signature What It Does
std::mem::swap(a, b) fn swap<T>(x: &mut T, y: &mut T) Exchanges the values at x and y in place without cloning or deallocating
std::mem::replace(dest, src) fn replace<T>(dest: &mut T, src: T) -> T Writes src into *dest and returns the old value of *dest as an owned T
std::mem::take(dest) fn take<T: Default>(dest: &mut T) -> T Replaces *dest with T::default() (such as an empty String or Vec) and returns the old owned T
use std::mem;

fn drain_message(slot: &mut String) -> String {
    // Takes ownership of the String inside `*slot` and leaves `String::new()` in its place:
    mem::take(slot)
}

fn main() {
    let mut inbox = String::from("Urgent notification");

    let taken: String = drain_message(&mut inbox);
    println!("Taken owned string: '{taken}'");
    println!("Inbox after take:   '{inbox}' (len = {})", inbox.len());

    let previous = mem::replace(&mut inbox, String::from("New message"));
    println!("Replaced '{previous}' with '{inbox}'");
}
Taken owned string: 'Urgent notification'
Inbox after take:   '' (len = 0)
Replaced '' with 'New message'

Common Compiler Errors with &mut T

Two Simultaneous Mutable Borrows (E0499)

// ❌ does not compile
fn main() {
    let mut data = vec![1, 2, 3];

    let r1 = &mut data;
    let r2 = &mut data;

    r1.push(4);
    r2.push(5);
}
error[E0499]: cannot borrow `data` as mutable more than once at a time
 --> src/main.rs:5:14
  |
4 |     let r1 = &mut data;
  |              --------- first mutable borrow occurs here
5 |     let r2 = &mut data;
  |              ^^^^^^^^^ second mutable borrow occurs here
6 |
7 |     r1.push(4);
  |     -- first borrow later used here

error: aborting due to 1 previous error

Reading the Owner While a Mutable Borrow Is Still Active (E0502)

Even reading data via println!("{data:?}") temporarily borrows &data. Doing so while r1: &mut Vec<i32> is still alive and used on a later line violates r1's exclusivity:

// ❌ does not compile
fn main() {
    let mut count = 10;
    let r = &mut count;

    println!("Reading owner directly while `r` is active: {count}");
    *r += 1;
}
error[E0502]: cannot borrow `count` as immutable because it is also borrowed as mutable
 --> src/main.rs:5:59
  |
3 |     let r = &mut count;
  |             ---------- mutable borrow occurs here
4 |
5 |     println!("Reading owner directly while `r` is active: {count}");
  |                                                           ^^^^^^^ immutable borrow occurs here
6 |     *r += 1;
  |     ------- mutable borrow later used here

error: aborting due to 1 previous error