Variables & Mutability
Variables Are Immutable by Default
Section titled “Variables Are Immutable by Default”When you declare a variable with let, it is immutable — you cannot assign to it a second time. This is not a limitation; it is a feature. Immutability by default means the compiler can catch unintended mutations and reason about your code more precisely.
fn main() { let x = 5; // x = 6; // error[E0384]: cannot assign twice to immutable variable `x` println!("x = {}", x);}To allow mutation, add mut after let:
fn main() { let mut y = 10; y = 20; // allowed because y is mut println!("y = {}", y);}Shadowing
Section titled “Shadowing”Shadowing is different from mutation. You re-declare a variable with the same name using a new let binding. The new binding shadows the old one for the rest of the scope. You can even change the type when you shadow.
fn main() { let z = 3; let z = z * 2; // shadows the previous z let z = z + 1; // shadows again println!("z = {}", z); // 7
// Shadowing can change the type let word = "hello"; let word = word.len(); // shadow with a usize println!("len = {}", word);}Shadowing is not the same as mut. With mut, you re-assign to the same variable. With shadowing, you create a new variable that happens to have the same name. After the scope ends, the outer binding comes back into view.
Constants
Section titled “Constants”const declares a compile-time constant. Unlike let, constants:
- require an explicit type annotation
- must be set to a constant expression (not a runtime value)
- are valid for the entire duration of the program in their scope
- use
SCREAMING_SNAKE_CASEby convention
const MAX_POINTS: u32 = 100_000;const GRAVITY: f64 = 9.81;
fn main() { println!("MAX_POINTS = {}", MAX_POINTS); println!("GRAVITY = {}", GRAVITY);}The underscore in 100_000 is a numeric separator — it is purely cosmetic and ignored by the compiler. It makes large numbers easier to read.
fn main() { let x = 5; // x = 6; // error: cannot assign twice to immutable variable
let mut y = 10; y = 20; println!("y = {}", y);
// Shadowing: re-bind the same name let z = 3; let z = z * 2; let z = z + 1; println!("z = {}", z);
// Constants are always immutable and require a type annotation const MAX_POINTS: u32 = 100_000; println!("MAX_POINTS = {}", MAX_POINTS);}Compiling…