Why does Rust require the mut keyword?
Short answer: let makes a binding you cannot reassign. Write let mut count = 0 when you need
to change the value after the binding is created. mut doesn’t change the type of anything — it’s a
property of the binding, not of the value.
The error you get without it
Section titled “The error you get without it”fn main() { let count = 0; count = count + 1; println!("{count}");}cargo build prints:
error[E0384]: cannot assign twice to immutable variable `count` --> src/main.rs:3:5 |2 | let count = 0; | ----- | | | first assignment to `count` | help: consider making this binding mutable: `mut count`3 | count = count + 1; | ^^^^^^^^^^^^^^^^^ cannot assign twice to immutable variable
For more information about this error, try `rustc --explain E0384`.The message says “assign twice” even though you assigned once after the initialiser — and the
diagnostic points at both lines. The let counts as the first assignment, so the reassignment is the
second.
The fix is one keyword
Section titled “The fix is one keyword”fn main() { let mut count = 0; count += 1; count += 1; println!("count = {count}");}count = 2(+= 1 is the same as count = count + 1; it’s the reassignment that needs the mut, not the
operator.)
What mut actually changes
Section titled “What mut actually changes”mut belongs to the variable name, not to the type. let x: i32 and let mut x: i32 are both
i32, and the type checker sees no difference between them: all you’ve declared is that this
particular name may be assigned to again.
The same is true of a mut in a parameter list:
fn double(mut n: i32) -> i32 { n *= 2; n}
fn main() { let n = 21; let f: fn(i32) -> i32 = double; println!("{}", f(n)); println!("{n} is unchanged");}4221 is unchangeddouble reassigns its copy of n as much as it likes. From the outside it’s an ordinary
fn(i32) -> i32 — the mut is part of the body, not the signature, and the caller’s let n = 21
needs no mut of its own.
Where else mut shows up
Section titled “Where else mut shows up”Reassignment is the obvious case. The other one is calling a method that changes the value:
fn main() { let numbers = vec![1, 2, 3]; numbers.push(4); println!("{numbers:?}");}error[E0596]: cannot borrow `numbers` as mutable, as it is not declared as mutable --> src/main.rs:3:5 |3 | numbers.push(4); | ^^^^^^^ cannot borrow as mutable |help: consider changing this to be mutable |2 | let mut numbers = vec![1, 2, 3]; | +++
For more information about this error, try `rustc --explain E0596`.Vec::push needs a mutable receiver, so the binding has to be declared mutable before the call is
even considered:
fn main() { let mut numbers = vec![1, 2, 3]; numbers.push(4); println!("{numbers:?}");}[1, 2, 3, 4]Why the default is the strict one
Section titled “Why the default is the strict one”A binding that can’t change is one the reader can rely on. Leaving mut off tells whoever opens the
function next that nothing below reassigns this name — in code you didn’t write, that’s one less
thing to trace.
So write let, and add mut only where the compiler asks for it. Both errors above are the compiler
asking. Sprinkling mut on every binding to pre-empt them costs you that signal and buys nothing.
Related
Section titled “Related”- Compiler errors — how to read a diagnostic, then one page per error code
- Why does Rust move values instead of copying them? — the other way a binding stops being usable
- The bits that trip everyone up — the rest of this chapter