Pattern Matching (match, if let, while let)

Pattern matching is Rust's way of testing a value's shape and pulling data out of it at the same time — the match expression checks a value against patterns and the compiler insists every case is covered.

Learn Pattern Matching (match, if let, while let) in our free Rust course — a beginner-friendly interactive lesson with worked examples, a practice exercise…

Part of the free Rust course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

In this lesson you'll learn exhaustive match , ranges and or-patterns, binding with @ , guards, destructuring enums and tuples, and the if let / let else / while let shorthands.

What You'll Learn in This Lesson

1️⃣ match: Arms, Ranges, Or-Patterns, Bindings

A match tries each arm top to bottom and runs the first that fits. Arms can be exact values, an or-pattern with | , an inclusive range with ..= , or the wildcard _ . The @ operator lets you bind the matched value while testing it. Crucially, a match must be exhaustive .

Because match is an expression, each arm yields a value you can return or bind — here every arm produces a string. Drop the final _ arm and the compiler refuses to build until every i32 is covered.

2️⃣ Destructuring & Guards

Patterns can reach inside structured data. You can destructure an enum variant to pull out its fields, a tuple into named pieces, or a struct 's fields by name. A guard — an if after the pattern — adds a condition the pattern syntax alone can't express.

3️⃣ if let, let else & while let

When you care about just one pattern, a full match is overkill. if let runs a block only on a match. let ... else binds variables on success and forces an early exit on failure. while let repeats as long as the pattern keeps matching.

Your turn. Fill in the blanks marked ___ , then run it.

Model events as an enum and dispatch them with one exhaustive match , destructuring each variant. Run it with cargo run and check the output.

📋 Quick Reference — Patterns

Practice quiz

What does it mean that a match must be exhaustive?

  • It must be fast
  • It must have one arm
  • Every possible value must be covered
  • It must use a guard

Answer: Every possible value must be covered. The compiler requires every possible value to be handled.

What does the _ pattern do in a match?

  • Is the catch-all wildcard
  • Matches nothing
  • Binds a variable
  • Causes an error

Answer: Is the catch-all wildcard. _ is the wildcard arm that matches anything not yet covered.

What does the pattern 4..=9 match?

  • 4 to 8
  • Only 4 and 9
  • 9 to 4
  • 4 to 9 inclusive

Answer: 4 to 9 inclusive. ..= is an inclusive range, matching 4 through 9.

What does the | symbol mean in 1 | 2 | 3?

  • Bitwise OR
  • An or-pattern matching any listed value
  • A guard
  • A range

Answer: An or-pattern matching any listed value. | is an or-pattern: it matches any of the listed values.

What does x @ 1..=5 do?

  • Tests the range and binds the value to x
  • Adds x and the range
  • Imports x
  • Nothing

Answer: Tests the range and binds the value to x. @ binds the matched value to x while testing the pattern.

What is a guard in a match arm?

  • A type annotation
  • A wildcard
  • An if condition after the pattern
  • A loop

Answer: An if condition after the pattern. A guard is an extra if condition the pattern alone cannot express.

When is if let preferred over a full match?

  • When handling many cases
  • When you care about only one pattern
  • When you need exhaustiveness
  • Never

Answer: When you care about only one pattern. if let is the idiomatic shortcut when only one pattern matters.

What must the else block of a let...else do?

  • Return Ok
  • Nothing
  • Bind a variable
  • Diverge (return, break, continue, or panic)

Answer: Diverge (return, break, continue, or panic). The else block must diverge when the pattern does not match.

What does while let Some(x) = stack.pop() do?

  • Runs once
  • Loops while the pattern keeps matching
  • Errors on None
  • Pops nothing

Answer: Loops while the pattern keeps matching. while let repeats as long as pop() keeps returning Some.

Why does a guarded arm not count toward exhaustiveness?

  • Guards are slow
  • Guards are deprecated
  • The compiler cannot reason about the guard condition
  • It always counts

Answer: The compiler cannot reason about the guard condition. Because the compiler cannot evaluate the guard, you still need a fallback arm.