Mutable vs. Immutable References

In this tutorial, you’ll learn the differences between mutable and immutable references.

In Rust, you can borrow values instead of moving ownership:

  • &TImmutable reference (read-only).
  • &mut TMutable reference (read & write).

Both give you access to the same value without taking ownership.

Immutable References (&) #

By default, references are immutable:

fn main() {
    let name = String::from("Alice");

    let r1 = &name; // borrow immutably
    let r2 = &name; // multiple immutable borrows are fine

    println!("r1: {}, r2: {}", r1, r2);
    println!("Original: {}", name); // still valid
}
Code language: Rust (rust)

You can have any number of immutable references at the same time. But you cannot change through them.

Mutable References (&mut) #

If you want to modify the borrowed value, you need a mutable reference. For example:

fn main() {
    let mut greeting = String::from("Hello");

    change(&mut greeting); // borrow mutably

    println!("Modified: {}", greeting);
}

fn change(s: &mut String) {
    s.push_str(", world!");
}
Code language: Rust (rust)

In this example:

  • let mut greeting makes the variable itself mutable.
  • &mut greeting allows the function to modify the string.
  • Ownership still stays in main.

Borrowing Rules Enforced by Rust #

Rust prevents dangerous patterns by enforcing three simple rules at compile time:

1. You can have multiple immutable references:

let r1 = &text;
let r2 = &text; 
// ✅ both fine
Code language: Rust (rust)

2. You can have only one mutable reference at a time:

let r1 = &mut text;
let r2 = &mut text; // ❌ ERROR: cannot borrow `text` mutably more than once
Code language: Rust (rust)

3. You cannot mix immutable and mutable references. For example, you cannot borrow text as mutable while also borrowed as immutable like this:

let r1 = &text;
let r2 = &mut text; // ❌ ERRORCode language: Rust (rust)

It is because:

  • Multiple immutable refs are safe → nothing changes.
  • A single mutable ref is safe → only one place can modify at a time.
  • Mixing would allow one part of your code to read while another is changing → data race.

Example: Compiler Enforces Safety #

fn main() {
    let mut value = 42;

    let r1 = &value;   // immutable borrow
    let r2 = &value;   // immutable borrow
    // let r3 = &mut value; // ❌ not allowed here

    println!("r1: {}, r2: {}", r1, r2);

    // After r1 and r2 are no longer used, mutable borrow is allowed
    let r3 = &mut value; 
    *r3 += 1; 
    println!("r3: {}", r3);
}
Code language: Rust (rust)

Rust tracks when references are last used, so it knows when it’s safe to create a mutable one.

Summary #

  • &T → Immutable reference: many allowed, read-only.
  • &mut T → Mutable reference: only one allowed, read & write.
  • No mixing mutable + immutable references at the same time.
  • Rust enforces these rules at compile time, preventing data races and memory bugs.
Was this Helpful ?