Skip to content

Pattern Matching

Hello, match!

If you came from a language with switch, throw away everything you know. Rust’s match is, no exaggeration, one of the most pleasant tools any language has ever shipped. It’s a switch that can also pull apart your data, that forces you to handle every case, and that produces a value you can assign.

🦀 main.rs

Notice:

  • 1 | 2 | 3or patterns. Match any of these.
  • 4..=9 — range patterns. Inclusive on both ends here.
  • _ if n < 0 — a guard on a wildcard, so we can add a runtime condition.
  • _ — wildcard. Matches anything we haven’t matched yet.

The compiler will yell at you if your match doesn’t cover every case. Try deleting the final _ => "large" arm and running it. The error message will literally list the patterns you missed. This exhaustiveness check is one of the great quality-of-life features of working in Rust.

Destructuring

The real fun starts when you match on a structured value and pull pieces out:

🦀 main.rs

You can destructure tuples and structs too:

🦀 main.rs

if let — when you only care about one case

Sometimes a match is overkill because you only care about one variant. That’s where if let shines:

🦀 main.rs

There’s also while let for “keep going as long as this pattern matches”:

🦀 main.rs

pop() returns Some(value) until the stack is empty, then None. The while let loop stops as soon as it sees None. Try changing it to a regular while loop and see how much uglier it gets.

let else — assign or bail out

Sometimes you want to extract a value from a pattern or leave the function early if it doesn’t match. That’s let else:

🦀 main.rs

This is incredibly handy for the “early return on failure” pattern that other languages do with exceptions.

A worked example — tiny calculator

Let’s pattern-match our way through evaluating a tiny expression tree:

🦀 main.rs

Recursive data types like this — trees, lists, expression languages — are made for pattern matching. The shape of the data and the shape of the code look the same, which is one of those things that feels obvious in hindsight but absolutely shines once you have it.

Onward: how Rust handles errors without exceptions.