Variables & Data Types
Variables in Rust are immutable by default — a genuine surprise if you're coming from almost any other language on this site. Once you bind a value with let, you can't change it unless you say so explicitly with mut.
let is immutable unless you say mut
fn main() { let score = 88; println!("Score: {}", score); score = 92; println!("Score: {}", score); }
error[E0384]: cannot assign twice to immutable variable `score` --> src/main.rs:5:5 | 2 | let score = 88; | ----- | | | first assignment to `score` | help: consider making this binding mutable: `mut score` ... 5 | score = 92; | ^^^^^^^^^^^ cannot assign twice to immutable variable
This isn't a warning — it's a compile error, and the program never runs. Rust defaults to immutable on purpose: most variables never actually need to change after they're set, and marking the few that do makes those spots easy to spot when reading code later. Fix it by adding mut:
fn main() { let mut score = 88; println!("Score: {}", score); score = 92; println!("Score: {}", score); }
Score: 88 Score: 92
Basic types
Rust infers types most of the time, but every value still has one, decided at compile time — nothing changes at runtime:
fn main() { let age: i32 = 30; // signed 32-bit integer let price: f64 = 19.99; // 64-bit float let initial: char = 'R'; // a single character let active: bool = true; let name = "Priya"; // inferred as &str println!("{} is {}, active: {}", name, age, active); }
Priya is 30, active: true
i32 is the default integer type if you don't specify one, and f64 the default float type. "Priya"'s type is &str — a reference to string data — rather than a full-blown String; the difference between the two matters a lot once you reach ownership, and gets its own note there.
Shadowing
Rust also lets you declare a new variable with the same name as an old one — shadowing it — which is different from mutation:
fn main() { let spaces = " "; let spaces = spaces.len(); println!("{}", spaces); }
3
let creates a brand-new binding — it can even change type, as it does above (from &str to usize). This is different from mut, which keeps the same variable and the same type but allows its value to change. Reach for mut when a value genuinely changes over time; reach for shadowing when you're transforming a value once and don't need the old form anymore.