Coming from Python or JavaScript, the first real Rust surprise is this: variables are immutable by default. let x = 5; binds a value you cannot change. Want to change it? You opt in with mut.
let and mut
let x = 5; is fixed. let mut x = 5; can be reassigned. This isn't bureaucracy — immutability is the default because most values shouldn't change, and the compiler can reason far more aggressively about things it knows are stable. You mark mutation exactly where it happens, and a reader sees it instantly.
const — compile-time constants
const MAX: u32 = 100_000; is a true constant: always immutable, must carry a type annotation, and is computed at compile time. Use const for values that are conceptually fixed forever (limits, conversion factors); use let for everything local.
Shadowing — the same name, a new value
You can declare a new variable with the same name as an old one. This isn't mutation — it's a brand-new binding that shadows the previous one, and it can even change type. It's the idiomatic way to transform a value through stages while keeping one good name.
Type inference
Rust is statically typed but rarely makes you say so. let x = 5; infers i32. You annotate only when the compiler can't figure it out (like parsing a string into a number) or when you want to be explicit for clarity.