How to fix error E0384: cannot assign twice to immutable variable
Short answer: the variable was declared without mut. Change let x = 5; to
let mut x = 5; — mut goes on the declaration, and nowhere else.
The error
Section titled “The error”fn main() { let x = 5; x = 6; println!("{x}");}Compiling that with cargo build prints:
error[E0384]: cannot assign twice to immutable variable `x` --> src/main.rs:3:5 |2 | let x = 5; | - first assignment to `x`3 | x = 6; | ^^^^^ cannot assign twice to immutable variable |help: consider making this binding mutable |2 | let mut x = 5; | +++The help block is the whole fix: put mut after let on the line where the variable is declared.
Why the let counts as an assignment
Section titled “Why the let counts as an assignment”The message says assign twice, which sounds odd when you only wrote one =. The reason is that
let x = 5; is itself an assignment — the first one. Rust gives every let binding exactly one
assignment unless you mark it mut.
You can see this by splitting the declaration and the assignment apart:
fn main() { let x; // declare with no value x = 5; // the first (and only) assignment — this compiles println!("{x}");}That runs and prints 5. No mut needed, because it is still the first assignment. A second x = 6;
after it is what E0384 objects to.
The fix
Section titled “The fix”fn main() { let mut x = 5; x += 1; println!("{x}");}This prints 6. Compound assignments trigger the same error too: total += 5; on a let total = 0;
binding fails with E0384 just like total = 5; would, and the same mut fixes it.
Where mut goes
Section titled “Where mut goes”mut always sits with the binding, not the assignment. On a let line:
let mut total = 0;On a function parameter, when the function body wants to reuse its own copy:
fn digit_sum(mut n: u32) -> u32 { let mut sum = 0; while n > 0 { sum += n % 10; n /= 10; } sum}
fn main() { println!("{}", digit_sum(1234));}This prints 10. mut n: u32 means the function owns its own copy and is allowed to change it —
n /= 10 needs it. The caller passes a plain value as usual; the caller’s variable is unaffected
either way, because u32 is copied, not moved.
mut or shadowing?
Section titled “mut or shadowing?”For “give the variable a new value”, Rust offers two tools, and they are not the same:
fn main() { let x = 5; let x = x + 1; // a new variable that reuses the name let x = x.to_string(); // the type can even change println!("{x}");}This prints 6. Shadowing declares a fresh variable each time; the old one still existed until the
new one covered it up. That is why the type is allowed to change from i32 to String.
Pick shadowing when the new value replaces the old for good — conversions, re-parsing, the usual
let input = input.trim(); chain. Pick mut when you are building one value up in place: counters,
accumulators, loops that mutate as they go. If the variable’s type never changes and every
“reassignment” is really a step in a computation, mut is the honest spelling.
Not this error
Section titled “Not this error”Two neighbours that look identical but carry different codes:
- Assigning to a field of an immutable struct —
p.x = 10;wherepislet p = ...— fails with E0594 (cannot assign top.x, aspis not declared as mutable). Same missing-mutcause, different message. - Calling a method that needs
&muton an immutable binding is E0596, and it is about borrowing rather than assignment.
Related errors
Section titled “Related errors”- E0382 — borrow of moved value
E0594— cannot assign to a field of an immutable structE0596— cannot borrow as mutable, as it is not declared as mutable