Skip to content
Select theme

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.

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.

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:

This prints:

count is 42

Nothing 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 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:

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:

This prints:

ada now has 10

One 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.

A &mut self method is the same borrow in disguise. Calling it mutates through the receiver, so the receiver’s binding needs mut:

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:

This prints:

n is 2

Function parameters can carry mut too, but it means something narrower than &mut:

This prints:

bumped to 41
score is still 40

mut 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.

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.

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 missing mut, different operation.
  • Taking a second &mut while 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 &mut is E0502 (cannot borrow as mutable because it is also borrowed as immutable). Here nothing else borrows the value — the binding itself is the problem.