Variables and Mutability

Common Mutability Errors

Because Rust enforces immutability by default and strictly separates binding mutability (let mut x) from reference mutability (&mut T), the compiler catches invalid mutations and unnecessary mut annotations at compile time.

Quick Reference of Mutability Diagnostics

Error / Warning Code Message Summary Root Cause Fix
error[E0384] cannot assign twice to immutable variable Reassigning x = ... when x was declared with let instead of let mut Declare let mut x = ... (or shadow with let x = ...)
error[E0596] cannot borrow 'x' as mutable, as it is not declared as mutable Taking &mut x or calling a &mut self method (x.push(...)) on an immutable x Declare let mut x = ...
error[E0594] cannot assign to '*r', which is behind a '&' reference Trying to mutate through a shared reference (r: &i32 or r: &mut &i32) Change the reference type to &mut T
error[E0502] cannot borrow 'x' as mutable because it is also borrowed as immutable Creating &mut x while an active &x reference is still in use Finish using &x before taking &mut x
error[E0499] cannot borrow 'x' as mutable more than once at a time Holding two active &mut x references simultaneously Keep only one active &mut x at a time (or use .split_at_mut())
warning: unused_mut variable does not need to be mutable Declaring let mut x when x is never reassigned or mutably borrowed Remove the mut keyword

1. Reassigning an Immutable Variable (E0384)

Attempting to assign a new value to a variable declared without mut fails with E0384:

// ❌ does not compile
fn main() {
    let retries = 3;
    retries = 4;
}
error[E0384]: cannot assign twice to immutable variable `retries`
 --> src/main.rs:3:5
  |
2 |     let retries = 3;
  |         -------
  |         |
  |         first assignment to `retries`
  |         help: consider making this binding mutable: `mut retries`
3 |     retries = 4;
  |     ^^^^^^^^^^^ cannot assign twice to immutable variable

error: aborting due to 1 previous error

2. Calling a Mutating Method on an Immutable Variable (E0596)

Methods that modify a value in place—such as Vec::push, String::push_str, or slice::sort—take &mut self. Even if you are not reassigning the variable with =, calling a &mut self method requires the owner binding to be declared let mut:

// ❌ does not compile
fn main() {
    let text = String::from("Hello");
    text.push_str(", world!");
}
error[E0596]: cannot borrow `text` as mutable, as it is not declared as mutable
 --> src/main.rs:3:5
  |
2 |     let text = String::from("Hello");
  |         ---- help: consider changing this to be mutable: `mut text`
3 |     text.push_str(", world!");
  |     ^^^^ cannot borrow as mutable

error: aborting due to 1 previous error

3. Mutating a Struct Field When the Struct Binding Is Immutable (E0594)

Rust does not support per-field mut modifiers inside a struct definition. Mutability is inherited by all fields from the binding that holds the struct:

// ❌ does not compile
struct Counter {
    value: u32,
}

fn main() {
    let counter = Counter { value: 0 };
    counter.value += 1;
}
error[E0594]: cannot assign to `counter.value`, as `counter` is not declared as mutable
 --> src/main.rs:7:5
  |
6 |     let counter = Counter { value: 0 };
  |         ------- help: consider changing this to be mutable: `mut counter`
7 |     counter.value += 1;
  |     ^^^^^^^^^^^^^^^^^^ cannot assign

error: aborting due to 1 previous error

4. Confusing Binding Mutability (mut r) with Target Mutability (&mut T)

When a variable holds a reference, mut can appear in two different positions with completely different meanings:

  let mut r: &i32      --> `r` can be reassigned to point to a different `i32`,
                           but CANNOT modify the `i32` it points to!

  let r: &mut i32      --> `r` always points to the same `i32`,
                           and CAN modify that `i32` via `*r = ...`!

  let mut r: &mut i32  --> `r` can BOTH modify the target `i32` AND be reassigned
                           to point to a different `&mut i32`.
Declaration Rebind r = other_ref; Allowed? Mutate Target *r = 99; Allowed?
let r: &i32 No (E0384) No (E0594)
let mut r: &i32 Yes No (E0594)
let r: &mut i32 No (E0384) Yes
let mut r: &mut i32 Yes Yes

Trying to modify the target through let mut r: &i32 fails with E0594 because the reference itself is still a shared immutable reference (&i32):

// ❌ does not compile
fn main() {
    let x = 10;
    let mut r: &i32 = &x;
    *r = 20;
}
error[E0594]: cannot assign to `*r`, which is behind a `&` reference
 --> src/main.rs:4:5
  |
3 |     let mut r: &i32 = &x;
  |                ---- help: consider changing this to be a mutable reference: `&mut i32`
4 |     *r = 20;
  |     ^^^^^^^ `r` is a `&` reference, so the data it refers to cannot be written

error: aborting due to 1 previous error

Conversely, let r: &mut i32 (without mut before r) can mutate *r = 20; because r holds an exclusive &mut i32 reference:

fn main() {
    let mut x = 10;
    let r: &mut i32 = &mut x; // `r` itself is not `mut`, but its target is `&mut i32`

    *r = 20; // Valid! Mutates `x` through the `&mut i32` reference
    println!("x = {x}");
}
x = 20

5. Attempting to Mutate a const Item

Constants declared with const are compile-time values inlined directly into every usage site; they have no mutable memory address and cannot be declared const mut or assigned to:

// ❌ does not compile
const LIMIT: i32 = 10;

fn main() {
    LIMIT = 20;
}
error[E0070]: invalid left-hand side of assignment
 --> src/main.rs:4:11
  |
4 |     LIMIT = 20;
  |     ----- ^
  |     |
  |     cannot assign to this expression

error: aborting due to 1 previous error

6. Unnecessary mut Warning (unused_mut)

If you declare a variable with let mut but never mutate it or borrow it as &mut, rustc warns that mut is redundant so readers are not misled into thinking the variable changes later in the function:

fn main() {
    let mut fixed = 42;
    println!("{fixed}");
}
warning: variable does not need to be mutable
 --> src/main.rs:2:9
  |
2 |     let mut fixed = 42;
  |         ----^^^^^
  |         |
  |         help: remove this `mut`
  |
  = note: `#[warn(unused_mut)]` on by default