Variables are immutable by default — use mut when you need reassignment. Rust is statically typed with full inference inside functions.
Core concepts
- let & mut: Immutability prevents accidental shared mutation across threads. — The Rust Book: Variables and mutability
- Scalars: integers (with explicit sizes),
f64,bool,char(Unicode scalar). — The Rust Book: Scalar types - Compounds: tuples, arrays (fixed size), slices
&[T]. — The Rust Book: Compound types - Control flow:
ifis an expression;loop,while,for. — The Rust Book: Control flow - match: Exhaustive pattern matching — compiler enforces all cases. — The Rust Book: match
fn grade(score: u8) -> &'static str {
match score {
90..=100 => "A",
80..=89 => "B",
_ => "C",
}
}Concept videos
YouTube lessons for these topics. On a large screen they open in the reading panel next to the roadmap, same as the article links on each core concept.
- Rust basics — Rust in 100 Seconds (Fireship)
Explanation
Rust defaults to immutable variables like a whiteboard you cannot erase without explicit permission (mut). That default protects you when code later runs on many threads.
let & mut
Immutability means rebinding is forbidden unless you declare mut. Shadowing (let x = ...; let x = ...) creates a new binding — different from mutation. The compiler uses immutability to reason about safety.
Scalars
Integer types include size (i32, u64) — choose based on range needs. f64 is default float; bool is two-valued; char is a Unicode scalar (four bytes). Explicit sizes matter for systems and FFI work.
Compounds
Tuples group heterogeneous values; arrays have fixed length known at compile time. Slices &[T] are views into contiguous memory without copying — foundational for strings and buffers.
Control flow
if returns values — assign its result to variables. loop is infinite until break with value; while and for cover usual cases. for iterates iterators ergonomically over collections.
match
match is a decision tree the compiler checks for completeness. Missing cases fail to compile — unlike switch fall-through bugs in C. Use _ for catch-all patterns responsibly.
What Is It?
Rust is statically typed with inference — types checked at compile time.
Scalars and compounds (tuples, arrays) combine with enums and structs for data modeling.
Control flow uses if expressions, loops, and powerful match patterns.
Why Should I Care?
Type errors caught at compile time prevent entire classes of runtime bugs.
Pattern matching models domain states clearly (Option, Result enums later).
Real World Analogy
Immutability by default is Rust telling you 'read the recipe before you change the soup' — you must say mut when you intend to stir.
Visual Mental Model
let x = 5; (immutable)
let mut y = 5; (mutable binding)Beginner Explanation
Variables bind immutably unless mut. Pattern matching with match/exhaustive arms replaces many switch pitfalls.
Smallest Possible Example
fn grade(score: u8) -> &'static str {
match score {
90..=100 => "A",
_ => "keep going",
}
}Common Mistakes
❌ Overusing clone() to silence borrow errors.
Clone fixes symptoms. Learn why the borrow failed first.
❌ Non-exhaustive match arms.
Compiler rejects them — list all enum variants or use wildcard consciously.
❌ Confusing &str and String.
String owns heap data; &str borrows a slice. APIs often take &str for flexibility.
Real Project Example
Servo and rustc itself demonstrate Rust's type system at scale.
Game engines like Bevy use enums for entity states with match.
Build This Mini Project
Model a traffic light as an enum and implement next_state with match — no if-else chain.
Interview Question
Why immutability by default?
It makes data races and accidental mutation harder and documents intent — mutability is explicit where needed.
Key Takeaways
- Prefer let; use mut deliberately.
- match for exhaustive branching.
- String vs &str — know ownership tie-in.
- Types catch bugs before run.
Resources
The 60-second story
A workshop bench where every tool is bolted down unless you unlock it. let x = 1 bolts the hook. You can read x. You cannot casually rehang a different tool there. let mut x = 1 is the unlocked hook. That default feels fussy until two people share a bench and one of them “just tweaks” a number the other person was measuring.
Scalars are the single tools: integers with a size written on them (i32, u64), f64 for fractions, bool for yes or no, char for one Unicode character, not “a byte and good luck.” The size is on the tool because systems work cares whether the wrench is 32 millimeters or 64. Guessing is how you smash a bolt.
Compounds are kits. A tuple is a small pouch with a fixed set of different tools. An array is a row of the same tool, length known up front. A slice &[T] is looking at part of that row without taking the tools home.
if in Rust can hand you a value, not only choose a path. You can write let label = if score > 90 { "A" } else { "B" }; the way you would write a formula. loop, while, and for are how you repeat without copying the bench.
match is a sorting table that refuses to let a package leave until every shape has a bin. Forget a bin and the shop does not open. That is the compiler being a strict floor lead, not a linter with opinions.
Map the bench.
letis a bolted hook.mutis permission to rehang.- Scalars are single tools with sizes.
- Tuples, arrays, and slices are pouches, rows, and windows onto rows.
ifas an expression is a formula that yields a tool.matchis the sorting table that must cover every shape.
fn label(score: u8) -> &'static str {
if score > 90 { "A" } else { "B" }
}The function’s return type is part of the sign on the bench. Callers do not guess. Inference lets you skip some labels inside the function. The edges stay labeled so the next bench knows what it is receiving.
Rust made immutability the default because shared mutation is how benches collide. It made match exhaustive because a missing case in C is often a fall-through that ships. The language would rather refuse to build than let you discover the missing bin on a Friday.
Mental model: bolted hooks, sized tools, and a sorting table that will not shrug. If a value should change, you said mut out loud.
You have the page when you can explain why let without mut is the normal case, and why a match that skips a shape is a build failure instead of a shrug.
