We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Shape Your Data: Structs, Enums, Pattern Matching step 7 of 26
match and exhaustiveness: the compiler as a checklist
Classify spreadsheet cell contents. Each input string becomes one of five
Cell variants, and each variant renders to a label.
pub fn classify(inputs: Vec<String>) -> Vec<String>
| input | variant | label |
|---|---|---|
"" |
Empty |
empty |
42 |
Number(42) |
number(42) |
true / false |
Flag(bool) |
flag(true) |
=A1+B1 |
Formula("A1+B1") |
formula(A1+B1) |
| anything else |
Text(String) |
text(5) — the count of chars |
The parser is given. You write label, as one exhaustive match with no
wildcard arm.
Exhaustiveness is the payoff for sum types
A match on an enum must cover every variant. Miss one and the program does
not compile:
error[E0004]: non-exhaustive patterns: `Cell::Formula(_)` not covered
Read what that buys you. Add a sixth variant to Cell tomorrow — say
Error(String) — and rustc will walk you to every match in the codebase
that needs updating and name the missing case at each one. Not a runtime
surprise, not a test that happened to cover it, not a default: branch that
silently did the wrong thing. A list of edits.
Most languages the audience knows have no equivalent. A switch over
strings, a chain of instanceof, a visitor interface — all of them let you
add a case and ship the gap. This is the single feature most likely to
convert a Rust sceptic, and it only works if you do not defeat it. (The next
item is entirely about the way people defeat it.)
match is an expression
Every arm produces a value, and all arms must produce the same type
(E0308 if they do not). So a match is something you can assign, return,
or pass directly:
let label = match cell {
Cell::Empty => "empty".to_string(),
Cell::Number(number) => format!("number({number})"),
// ...
};
Note Cell::Number(number) — the pattern both tests the variant and
extracts its payload in one move. There is no separate “cast to the right
type” step, because the pattern already proved which variant it is.
The nastiest bug in this cluster
Write Empty => instead of Cell::Empty => and something quietly different
happens: a lowercase-or-not bare identifier in a pattern is a new binding,
not a variant test. It matches everything and shadows the name. Your first
arm becomes a catch-all and the other four are dead.
rustc catches this specific case because the name collides with a real variant:
error[E0170]: pattern binding `Empty` is named the same as one of the
variants of the type `Cell`
The starter ships that error — meet it once on purpose. But notice how narrow
the rescue is. Mistype it as Emtpy and there is no collision, so there is no
E0170. You get a silent catch-all, an unreachable_pattern warning on the
arms below it, and a wrong program. The habit worth building: always write
the enum name in the pattern.
Lints in this neighbourhood
single_match pushes a two-arm match where one arm is {} toward
if let. match_like_matches_macro pushes an arm-per-boolean match toward
matches!. match_single_binding complains about a match whose only arm
binds a name — that is a let. All three are on by default. They are worth
reading as one message: match is for choosing between shapes; when you
are not choosing, something shorter says it better.
Remember the grade is compile + tests + clippy -D warnings.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.