How to fix error E0596: cannot borrow as mutable, as it is not declared as mutable
Short answer: the code took a &mut to something whose binding was not declared mut. Mutability
in Rust belongs to the binding, not to the value or the type — so the fix is a mut on the let
that created it, not on the field, the parameter type, or the borrow itself.
The error
Section titled “The error”fn main() { let count = 40; let r = &mut count; *r += 2; println!("count is {count}");}Compiling that with cargo build prints:
error[E0596]: cannot borrow `count` as mutable, as it is not declared as mutable --> src/main.rs:3:13 |3 | let r = &mut count; | ^^^^^^^^^^ cannot borrow as mutable |help: consider changing this to be mutable |2 | let mut count = 40; | +++
For more information about this error, try `rustc --explain E0596`.count was created with a plain let, so the binding is immutable. Writing &mut count asks for
permission to change the value through this binding — and the compiler checks that against the
binding itself, which promised nothing of the sort. The help block points at exactly the line that
needs to change: the let two lines up.
Why the compiler insists
Section titled “Why the compiler insists”mut is not a property of the value; it is a property of the binding. That is why the same value
behaves completely differently one keyword later:
fn main() { let mut count = 40; let r = &mut count; *r += 2; println!("count is {count}");}This prints:
count is 42Nothing about 40 or i32 changed — only the binding did. The rule buys something real: anyone
reading let count = 40; knows that no code anywhere can turn count into something mutable,
because every path to mutation has to go through a binding, and this one said no. The compiler
enforces the promise instead of trusting the programmer to keep it.
The binding versus the field
Section titled “The binding versus the field”The version that catches people is a struct field. The borrow asks for &mut p.score, and the
natural guess is that the field needs mut — but Rust has no mut on field declarations, and
adding one to the wrong place does nothing:
struct Player { name: String, score: u32,}
fn main() { let p = Player { name: String::from("ada"), score: 0, }; let score = &mut p.score; *score += 10; println!("score is {score}");}error[E0596]: cannot borrow `p.score` as mutable, as `p` is not declared as mutable --> src/main.rs:11:17 |11 | let score = &mut p.score; | ^^^^^^^^^^^^ cannot borrow as mutable |help: consider changing this to be mutable | 7 | let mut p = Player { | +++
For more information about this error, try `rustc --explain E0596`.Read the error text closely: it does not say p.score is not declared as mutable — it says p
is not. A field has no mutability of its own; whether p.score can be mutated is decided entirely
by the binding p hangs off. So the fix is on the struct’s binding:
struct Player { name: String, score: u32,}
fn main() { let mut p = Player { name: String::from("ada"), score: 0, }; let score = &mut p.score; *score += 10; println!("{} now has {score}", p.name);}This prints:
ada now has 10One mut on p makes every field mutable through it. The same confusion in the other direction:
writing let mut score = &mut p.score; does not fix the error — that mut makes the
reference binding reassignable (so score could later point somewhere else), but the borrow
&mut p.score still asks p for mutation, and p still refuses.
The same rule for methods
Section titled “The same rule for methods”A &mut self method is the same borrow in disguise. Calling it mutates through the receiver, so
the receiver’s binding needs mut:
struct Counter { n: u32,}
impl Counter { fn bump(&mut self) { self.n += 1; }}
fn main() { let c = Counter { n: 0 }; c.bump(); println!("n is {}", c.n);}error[E0596]: cannot borrow `c` as mutable, as it is not declared as mutable --> src/main.rs:13:5 |13 | c.bump(); | ^ cannot borrow as mutable |help: consider changing this to be mutable |12 | let mut c = Counter { n: 0 }; | +++
For more information about this error, try `rustc --explain E0596`.The fix is identical — let mut c = Counter { n: 0 }; — and then the method calls work:
struct Counter { n: u32,}
impl Counter { fn bump(&mut self) { self.n += 1; }}
fn main() { let mut c = Counter { n: 0 }; c.bump(); c.bump(); println!("n is {}", c.n);}This prints:
n is 2mut in a parameter is a different thing
Section titled “mut in a parameter is a different thing”Function parameters can carry mut too, but it means something narrower than &mut:
fn bump(mut score: u32) -> u32 { score += 1; score}
fn main() { let score = 40; let bumped = bump(score); println!("bumped to {bumped}"); println!("score is still {score}");}This prints:
bumped to 41score is still 40mut score makes the parameter’s own binding mutable inside the function — the caller’s score
never changes, and the caller does not need mut at all. &mut is what crosses the function
boundary and reaches back into the caller’s binding; that is the version that triggers E0596 when
the caller’s binding is immutable.
When the binding cannot be mut
Section titled “When the binding cannot be mut”Sometimes the value lives behind a binding that genuinely cannot be declared mut — it is shared
with other code that only expects read access. Declaring everything mut to silence the error is
not the fix; it widens the mutation permission to every line of the scope. When mutation has to
happen through a shared value, that is the job of Cell and RefCell, which move the borrow check
from compile time to run time — a separate topic with its own trade-offs.
Not this error
Section titled “Not this error”Three neighbours that look similar but carry different codes:
- Assigning to the immutable variable directly (
count += 1;with no borrow) is E0384 (cannot assign twice to immutable variable). Same missingmut, different operation. - Taking a second
&mutwhile the first is still in use is E0499 (cannot borrow as mutable more than once). There the bindings are fine; the two borrows collide. - Holding a
&(a shared borrow) alive while taking the&mutis E0502 (cannot borrow as mutable because it is also borrowed as immutable). Here nothing else borrows the value — the binding itself is the problem.