Syntax, Types & Control Flow

Variables, mutability, scalars, compounds, control flow, and pattern matching.

Variables are immutable by default — use mut when you need reassignment. Rust is statically typed with full inference inside functions.

Core concepts

  1. let & mut: Immutability prevents accidental shared mutation across threads. — The Rust Book: Variables and mutability
  2. Scalars: integers (with explicit sizes), f64, bool, char (Unicode scalar). — The Rust Book: Scalar types
  3. Compounds: tuples, arrays (fixed size), slices &[T]. — The Rust Book: Compound types
  4. Control flow: if is an expression; loop, while, for. — The Rust Book: Control flow
  5. match: Exhaustive pattern matching — compiler enforces all cases. — The Rust Book: match
rust
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.

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

text
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

rust
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.

  • let is a bolted hook.
  • mut is permission to rehang.
  • Scalars are single tools with sizes.
  • Tuples, arrays, and slices are pouches, rows, and windows onto rows.
  • if as an expression is a formula that yields a tool.
  • match is the sorting table that must cover every shape.
rust
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.

Threads in the same domain as this chapter — go deeper on iHateReading without leaving the roadmap.

Web scraping cover

Web scraping

Fetch and parse web data — the baseline skill behind browser-facing agents.

Hosting under $100/month at 10k visitors cover

Hosting under $100/month at 10k visitors

Cut hosting burn for agent-heavy apps with sane infra choices.