On-Ramp to Rust

pattern matching and exhaustiveness

You have already seen Option and Result, which carry a value inside one of a few shapes — Some or None, Ok or Err. To use them you need a way to ask which shape is it, and pull out whatever is inside. C's switch handles a number or a single character and falls through if you forget a break; it cannot look inside a structured value or guarantee you covered every case. Rust's answer is the match expression: you list each shape the value could take, and for each you write what to do, often binding the inner data to a name in the same breath.

A match takes a value and tries each pattern in turn until one fits, then runs that branch. The patterns can do two jobs at once: they test the shape and they destructure it, naming the pieces inside. Writing match opt { Some(x) => ..., None => ... } checks whether opt is a Some, and if it is, binds the contained value to x so you can use it in that branch — no separate is it Some? then read the inside in two steps. Patterns nest and combine: you can match on specific values, on ranges, on the fields of a struct, on several alternatives at once with x | y, and add a final catch-all written _ for everything you did not name explicitly.

The feature that makes match more than a tidy switch is exhaustiveness: the compiler checks that your patterns cover every possible case, and if even one is missing it refuses to compile with non-exhaustive patterns. This is quietly powerful. It means you can never forget to handle None, or Err, or a newly added variant — the day someone adds a third shape to a type, every match that did not account for it stops compiling, pointing you at exactly the code that now needs updating. Why it matters: exhaustiveness turns a whole category of forgot-a-case bugs (the missing default in a C switch, the unhandled error path) from a silent runtime surprise into a compile-time checklist the compiler walks with you.

let n: Option<i32> = Some(3); let msg = match n { Some(0) => "zero".to_string(), Some(x) if x < 0 => format!("negative {x}"), Some(x) => format!("positive {x}"), None => "nothing".to_string(), }; // leaving out None would fail: non-exhaustive patterns

match tests and destructures in one step; the compiler insists every case is covered.

Exhaustiveness is the safety win, not just neat syntax: omit a case and the code will not compile. The _ arm is a deliberate catch-all — use it on purpose, not to silence the exhaustiveness check you actually want.

Also called
match expressionexhaustive matchdestructuring模式比對match 運算式窮盡比對