Build the mental model
Every variable declared with `let x = 5;` in Rust is immutable by default — trying to reassign it triggers a compiler error. This looks backwards compared to most languages, but it comes from Rust's safety-first philosophy: knowing a variable can never change lets the compiler automatically guarantee it's safe to share across concurrent code without a data race. To allow reassignment, you opt in explicitly with `let mut x = 5;`, so a reader scanning the code can instantly tell, just from the `mut` keyword, exactly where a variable is allowed to change. Shadowing is entirely different from `mut` — `let x = x + 1;` re-declares the same name with `let`, creating a brand-new variable that reuses the name, and it can even change the type (say, from a string to an integer), whereas `mut` can only change a variable's value, never its type. A useful mental model is a label maker: `mut` is a box whose contents you're allowed to swap out, while shadowing is sticking the same label onto a completely new box.
Connect it to a real scenario
Building the lesson content analyzer tool, you need to accumulate a running total like `total_word_count`, so declare it explicitly mutable with `let mut total_word_count = 0;` since its value grows as each file is scanned. Anywhere that variable gets passed to a function, scanning for the `mut` keyword instantly tells you who is allowed to change it. When you read raw file content as a string and then want to process a trimmed, lowercased version, shadowing fits naturally — `let content = content.trim().to_lowercase();` reuses one variable name instead of inventing a new name for every intermediate step.
Try the working example
fn main() {
let mut total_word_count = 0;
total_word_count += 120;
total_word_count += 85;
println!("Running total: {total_word_count}");
let raw = " Rust Ownership ";
let content = raw.trim().to_lowercase();
println!("Normalized: '{content}'");
}You print "Running total: 205" and "Normalized: 'rust ownership'" to the terminal.5-minute try-it
Start with `let mut score = 10;`, add 5 to it, and print it — then use shadowing with `let score = score.to_string();` to convert it to a String type and print it again.
One important caution
Forgetting to add `mut` when a variable needs reassignment and hitting the "cannot assign twice to immutable variable" compile error — a direct consequence of Rust's default immutability.
Treating shadowing as just alternate syntax for `mut` and repeatedly shadowing inside a loop — each shadow creates a brand-new variable, so accumulating a value across loop iterations needs `mut` instead.
The Rust Programming Language — Variables and Mutability — Rust