C.W.K.
Stream
Lesson 05 of 06 · published

The Many Shapes of Patterns

~11 min · enums, patterns, destructuring, guards

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

Patterns aren't just for enums — they're a language-wide mini-syntax for taking data apart. You've already used them in let and match; here's the fuller toolkit.

Destructuring

A pattern can mirror the shape of the data and pull out its pieces: destructure a tuple (x, y), a struct Point { x, y }, or an enum variant Some(n). The pattern's structure matches the value's structure, and the names you write become bindings to the inner pieces.

Wildcards, alternatives, ranges

_ matches and ignores a single value; .. ignores the rest of a struct or tuple. The or-pattern 1 | 2 | 3 matches any of several values in one arm. A range pattern 1..=5 matches anything in that inclusive range. Together they let one arm express a rich condition without a chain of ifs.

Match guards add runtime conditions. An if after a pattern — Some(n) if n > 0 => ... — only takes the arm when both the pattern matches AND the guard is true. Guards let patterns express conditions the shape alone can't.

Binding with @

The @ operator binds a value to a name and tests it against a pattern at once: id @ 1..=9 => ... checks that id is in 1–9 and binds it as id for use in the arm. It's the tool for 'I need both the test and the value.'

Why this matters

Rust leans on pattern matching the way other languages lean on if-else chains. Mastering patterns is what makes idiomatic Rust read cleanly: complex case analysis becomes a flat, exhaustive match instead of nested conditionals.

Code

Destructure, guard, or-pattern, range, and @·rust
struct Point { x: i32, y: i32 }

fn classify(p: Point) -> &'static str {
    match p {
        Point { x: 0, y: 0 } => "origin",         // exact destructure
        Point { x, y: 0 } if x > 0 => "east axis", // destructure + guard
        Point { x: 0, .. } => "y axis",            // .. ignores the rest
        _ => "elsewhere",
    }
}

fn main() {
    let n = 7;
    let label = match n {
        0 => "zero",
        1 | 2 | 3 => "small",        // or-pattern
        x @ 4..=9 => { let _ = x; "mid" }, // range + binding with @
        _ => "big",
    };
    println!("{} {}", classify(Point { x: 5, y: 0 }), label);
}

External links

Exercise

Write a match over an i32 score that uses: an or-pattern for a few exact values, a range pattern 0..=59 for failing, a guard for an edge case, and @ to bind and report the exact failing score. Confirm each arm fires for the right inputs.
Hint
Combine them: s @ 0..=59 => format!("fail: {s}") binds and ranges at once; a guard like n if n == 100 catches a perfect score before a broader range arm.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue
💛 by Ttoriwarm

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.