Lesson 5 of 50 · rust
Variables and Mutability
Duration: 12 minutes
Lesson 5 of 50 · rust
Duration: 12 minutes
rust\nfn main() {\n let x = 5;\n println!(\"x = {}\", x); // prints 5\n // x = 6; // ERROR!\n}\n\n\nTrying to modify an immutable variable produces a compiler error. This is a feature, not a limitation—the compiler prevents accidental mutations.\n\n## Mutable Variables\n\nExplicitly mark variables as mutable with mut:\n\nrust\nfn main() {\n let mut x = 5;\n println!(\"x = {}\", x); // prints 5\n x = 6;\n println!(\"x = {}\", x); // prints 6\n}\n\n\nThe mut keyword signals intent: "This variable will change." This self-documents code.\n\n## Why Immutability by Default?\n\n### Safety\nImmutable variables can't change unexpectedly.\n\n### Concurrency\nImmutable data is thread-safe. Multiple threads safely read immutable values without synchronization.\n\n### Reasoning About Code\nImmediate clarity about whether a value can change.\n\n## Variable Shadowing\n\nDeclare a new variable with the same name, creating a new binding:\n\nrust\nfn main() {\n let x = 5;\n let x = x + 1; // Shadow: new binding\n let x = x * 2; // Another shadow\n println!(\"x = {}\", x); // prints 12\n}\n\n\nShadowing differs from mutability:\n- Shadowing: Creates new variable (possibly different type)\n- Mutability: Changes value of same variable\n\nUse shadowing for transformations:\n\nrust\nfn main() {\n let input = \" 42 \";\n let input = input.trim(); // Shadow: trimmed string\n let input: i32 = input.parse().unwrap(); // Shadow: parse to number\n println!(\"Number: {}\", input + 10); // 52\n}\n\n\n## Constants vs Immutable Variables\n\nrust\nconst MAX_POINTS: u32 = 100_000; // Constant\nlet fixed_value = 42; // Immutable variable\n\n\n| Aspect | const | let |\n|--------|---------|-------|\n| Evaluated | Compile time | Runtime |\n| Type | Required | Inferred |\n| Scope | Global | Block |\n\nUse constants for compile-time known values.\n\n## Type Annotations\n\nExplicitly specify types, though Rust usually infers them:\n\nrust\nlet x = 5; // Inferred as i32\nlet x: i32 = 5; // Explicit\nlet mut y: f64 = 3.14; // Explicit on mutable\n\n\n## Practical Example\n\nrust\nfn main() {\n let username = \"alice\"; // Immutable\n println!(\"User: {}\", username);\n \n let mut score = 0; // Mutable for game tracking\n score += 10;\n score += 5;\n println!(\"Score: {}\", score);\n \n const WINNING_SCORE: i32 = 100;\n if score >= WINNING_SCORE {\n println!(\"Victory!\");\n }\n}\n\n\n## Best Practices\n\n1. Start immutable: Add mut only when necessary\n2. Shadow for transformations: When data changes type or meaning\n3. Constants for config: Fixed values used throughout code\n4. Clear intent: mut shows your intentions to other developers