Reading — step 1 of 5
Learn
If Expressions and match
Rust control flow has two headline differences from everything you've used: if is an expression (it produces a value), and match is a pattern-matching machine whose superpower is that the compiler proves you handled every case. Both change how you structure code, not just how you spell it.
if: no parens, always braces, produces values
if score >= 90 {
println!("A");
} else if score >= 80 {
println!("B");
} else {
println!("F");
}
Familiar shape, two rules: no parentheses around the condition, braces mandatory. And the condition must be a real bool — if 1 { doesn't compile; no truthiness, no memorizing falsy tables.
The expression part is where Rust departs:
let grade = if score >= 90 { "A" } else { "F" };
No ternary operator needed — if/else is the ternary, readable at any size. Note the missing semicolons inside the branches: a block's last expression, unterminated, is the block's value. (Both branches must produce the same type; the compiler checks.)
match: switch that finishes the job
let description = match day {
"Sat" | "Sun" => "weekend", // multiple patterns with |
"Fri" => "almost",
_ => "weekday", // _ = catch-all
};
Three properties make match the most-used construct in real Rust:
- It's an expression — arms produce values, as above.
- No fallthrough, no break — one arm runs, cleanly.
- Exhaustiveness: the compiler requires every possible value be covered. Match on a
boolwithout afalsearm, or (in chapter 5) on an enum missing a variant → compile error naming exactly what you forgot. Add a variant to an enum next month and everymatchin the codebase that needs updating fails to build until you update it. That's refactoring with a safety net no other mainstream language provides.
Patterns go well beyond literals:
match n {
0 => println!("zero"),
1..=9 => println!("single digit"), // inclusive range pattern
x if x < 0 => println!("negative"), // guard clause
_ => println!("big"),
}
Your exercise: FizzBuzz for one number
Multiples of 3 → Fizz, of 5 → Buzz, both → FizzBuzz, else the number. The classic ordering trap applies — check the "both" case first (first true branch wins; test 3 before 15 and the 15 case is unreachable). Two idiomatic shapes; pick either:
// if/else, specific-first
if n % 15 == 0 { println!("FizzBuzz") }
else if n % 3 == 0 { println!("Fizz") }
else if n % 5 == 0 { println!("Buzz") }
else { println!("{n}") }
// or the very-Rust tuple match — order still decides
match (n % 3 == 0, n % 5 == 0) {
(true, true) => println!("FizzBuzz"),
(true, false) => println!("Fizz"),
(false, true) => println!("Buzz"),
(false, false) => println!("{n}"),
}
The tuple match is worth writing once even if you keep the if/else: all four combinations, visibly enumerated, compiler-verified complete. That's the match mindset — make the cases a checklist the compiler audits.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…