Thuta Learning
Rust
BasicProgrammingbeginner

Variables, Mutability, and Shadowing

What you'll walk away with

  • Explain the core ideas behind Variables, Mutability, and Shadowing
  • Run the sample Rust code and verify its output
  • Apply the technique correctly to the Tutorial Platform and production scenarios

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

rust
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 should see
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 MutabilityRust

Easy traps

  • 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.
  • Validate sample code in a local or test environment before applying it to a production system.

Exercise

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.

You'll know it worked when: You print "Running total: 205" and "Normalized: 'rust ownership'" to the terminal.