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.
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.