# Variables, Mutability, Shadowing and Types — Rust

Source: https://www.skillbyai.com/en/rust/b-vars

> Explicit about change and size.

## let, mut, shadowing, numeric types

Variables are **immutable** unless declared `let mut`. **Shadowing** (`let x = x * 2;`) creates a new binding, even with a different type, without making anything mutable. Integer types state their size and sign (`i32`, `u8`, `u64`, `usize`); `as` casts explicitly and silently wraps when the value does not fit, while methods like `checked_add` return `None` on overflow. In debug builds plain arithmetic overflow panics; in release builds it wraps, so use checked or saturating methods when overflow is possible.

## Immutable by default, expressions everywhere

Rust variables are immutable unless marked mut, and most constructs are expressions that return values.

![Three ideas: variables and types, expressions and match, functions and modules.](assets/figures/rust/section-2-map.svg) — Figure 2.1 — Variables, expressions and functions.

## Mutability, shadowing and integer behaviour, run

I ran this with Rust 1.99.0 (cargo run, edition 2024, standard library only). count is mutable; x and spaces are shadowed (spaces changes from text to a number). Casting 5,000,000,000 to u32 wraps to 705,032,704, and checked_add returns None when 250 + 10 overflows a u8.

```rust
fn main() {
    let x = 5;                 // immutable by default
    let mut count = 0;         // opt in to mutation
    count += x;
    let x = x * 2;             // shadowing: a new binding with the same name
    let spaces = "   ";
    let spaces = spaces.len(); // shadowing can even change the type
    let big: u64 = 5_000_000_000;
    let small = big as u32;    // explicit cast; wraps on overflow
    let tuple: (i32, f64, char) = (1, 2.5, 'R');
    println!("count={count} x={x} spaces={spaces}");
    println!("big={big} as u32={small} tuple.2={}", tuple.2);
    println!("checked add: {:?} {:?}", 250u8.checked_add(5), 250u8.checked_add(10));
}
```

Output:

```
count=5 x=10 spaces=3
big=5000000000 as u32=705032704 tuple.2=R
checked add: Some(255) None
```

## Avoid lossy as casts

Prefer u32::try_from(big) which returns an error instead of silently wrapping.

**Quiz:** What does let x = x * 2; do when x already exists?

- [ ] Mutates the original x
- [x] Shadows x with a new binding
- [ ] Causes a compile error
- [ ] Creates a reference to x

*Answer:* Shadows x with a new binding. Shadowing is not mutation.
