Variables and Types
Scope: declaring variables, mutability, shadowing, and the types you'll use 95% of the time. All examples were compiled and run.
Declaring variables
let age = 42;
let name = "Ada";
let introduces a variable. Rust figures out the type itself
(type inference) — you rarely write types down.
Immutable by default
let x = 5;
// x = 6; // ERROR: cannot assign twice to immutable variable
To allow change, say mut:
let mut x = 5;
x = 6; // fine
This is deliberate: when you read someone's code (or your own, six
months later), a variable without mut is guaranteed never to
change. That's information the compiler enforces.
Shadowing: same name, new variable
let x = 5;
let x = x + 1; // a NEW variable x, shadowing the old one
let x = "five"; // even the type can change
Shadowing (let again with the same name) is different from
mutation: the old variable still exists underneath, unchanged, but
the name now points to the new one. It's commonly used to transform
a value step by step:
let input = " 42 ";
let input = input.trim();
let input: i32 = input.parse().unwrap();
The types you'll actually use
| Type | Meaning | Example |
|---|---|---|
i32 |
32-bit integer (the default) | let n = 42; |
i64 |
64-bit integer | let big: i64 = 9_000_000_000; |
u32 |
Unsigned (no negatives) 32-bit | let count: u32 = 7; |
f64 |
64-bit decimal (the default) | let pi = 3.14159; |
bool |
true/false | let done = false; |
char |
One character (Unicode) | let c = '🦀'; |
String |
Growable text (more in Ownership) | let s = String::from("hi"); |
&str |
Text slice — a view into text | let s = "hi"; |
A few notes:
- Integers:
i32vsu32vsi64... the letter is signed (i) or unsigned (u), the number is bits. Default integer type isi32; default decimal type isf64. Underscores in numbers are ignored:1_000_000reads better than1000000. charvs&strvsString: single quotes are one character ('a'), double quotes are text ("a"). Rust has two text types — this confuses everyone at first and is explained properly in Ownership.- Tuples group values:
let t = (42, "Ada", true);— access witht.0,t.1,t.2. - Arrays are fixed-size:
let a = [1, 2, 3];— always exactly 3 elements. (Growable lists areVec, covered later.)
Constants
const MAX_USERS: u32 = 100;
const values never change, must have their type written out, and
can live anywhere (even outside functions). Use them for magic
numbers you'd otherwise scatter through the code.
Type annotations: when to write them
You can always write the type:
let x: i32 = 5;
You usually don't need to. Write them when:
- The compiler asks you to (it will, clearly)
- Parsing text into a number:
let n: i32 = "42".parse().unwrap(); - A reader would otherwise guess wrong
Check
- [ ] You can explain
letvslet mutvs shadowing - [ ] You know the default integer (
i32) and decimal (f64) types - [ ] You can say why Rust has both
Stringand&str(short version: one owns its text, the other borrows a view — full story in Ownership)
Next: Ownership — the chapter everything else builds on.