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