Variables, Mutability & Constants
Prerequisites: How Rust code runs
Syntax
let x = 5; // immutable: can't be changed
let mut count = 0; // mutable: can be changed
let price: f64 = 9.99; // with an explicit type
const MAX_USERS: u32 = 100;
Variables are declared with let and are immutable by default —
once a value is bound to the name, it can't change. Add mut when you
need to change it. Defaulting to immutable makes code easier to reason
about: if a variable isn't mut, you know its value never changes.
The compiler usually infers the type from the value. You can also write it explicitly after a colon.
Shadowing means declaring a new variable with the same name using
let again. The new variable replaces the old one from that point on,
and it can even have a different type:
let input = "42";
let input: i32 = 42; // a new `input`, now a number
Constants (const) are always immutable, must have a type
annotation, and must be set to a value known at compile time. By
convention their names are SCREAMING_SNAKE_CASE. Constants can be
declared outside any function, making them available everywhere in the
file.
Inside a println! string, {name} prints the variable name —
the Printing & Formatting lesson covers this in detail.
Example
const MAX_LEVEL: u32 = 10;
fn main() {
let name = "Ada";
println!("Player: {name}");
let mut level = 1;
println!("Level: {level}");
level += 1;
level += 1;
println!("Level: {level} of {MAX_LEVEL}");
let spaces = " ";
let spaces = spaces.len(); // shadowing: now a number
println!("Spaces: {spaces}");
let x = 5;
{
let x = x * 2; // shadows `x` only inside this block
println!("Inner x: {x}");
}
println!("Outer x: {x}");
}
Expected output
Player: Ada
Level: 1
Level: 3 of 10
Spaces: 3
Inner x: 10
Outer x: 5
Common mistakes
- Changing a variable that isn't
mut:
let x = 5;
x = 6;
error[E0384]: cannot assign twice to immutable variable `x`
The compiler's help line suggests the fix: let mut x = 5;.
- Leaving the type off a constant.
const MAX = 10;fails with:
error: missing type for `const` item
- Confusing shadowing with mutation.
let x = x + 1;creates a new variable;x = x + 1;changes an existingmutvariable. Shadowing can change the type; mutation can't. - Ignoring "unused variable" warnings. They often point to a typo or
logic mistake. Prefix a name with
_(like_temp) if it's unused on purpose.
Practice
Declare a constant SECONDS_PER_MINUTE set to 60. Then create a
mutable variable total_seconds starting at 0, add 3 minutes' worth
of seconds to it, then 30 more seconds, and print
Total: 210 seconds.
Show solution
const SECONDS_PER_MINUTE: u32 = 60;
fn main() {
let mut total_seconds = 0;
total_seconds += 3 * SECONDS_PER_MINUTE;
total_seconds += 30;
println!("Total: {total_seconds} seconds");
}