08 / 20 · Day 3
Day 3 · Concept 08

Pattern matching

match is exhaustive and destructuring. Structs, enums, tuples, arrays, slices, references — all can be matched on, all in one syntax. The single feature that makes Rust feel like a typed Haskell.


1 · Destructure anything

rust src/main.rs
fn main() {
    // Tuple destructure
    let point = (3, 4);
    let (x, y) = point;
    println!("x={} y={}", x, y);

    // Array destructure with rest
    let arr = [1, 2, 3, 4, 5];
    if let [first, .., last] = arr {
        println!("first={} last={}", first, last);
    }

    // Struct destructure
    struct User { name: String, age: u32 }
    let u = User { name: "Alice".into(), age: 30 };
    let User { name, age } = u;
    println!("name={} age={}", name, age);
}

2 · Match guards and ranges

rust src/main.rs · advanced patterns
fn describe(n: i32) -> &'static str {
    match n {
        0                  => "zero",
        1..=9              => "single digit",
        10..=99            => "two digits",
        n if n < 0          => "negative",
        _                  => "big",
    }
}

fn main() {
    for n in [0, 5, 42, 1000, -7] {
        println!("{} -> {}", n, describe(n));
    }
}

3 · Binding with @ — capture while matching

rust src/main.rs
enum Msg {
    Hello { id: u32 },
}

fn handle(m: Msg) {
    match m {
        Msg::Hello { id: id_var @ 1..=10 } => println!("small id: {}", id_var),
        Msg::Hello { id: id_var @ 11..=100 } => println!("medium id: {}", id_var),
        Msg::Hello { id } => println!("other: {}", id),
    }
}

fn main() {
    handle(Msg::Hello { id: 5 });
    handle(Msg::Hello { id: 50 });
    handle(Msg::Hello { id: 999 });
}

4 · let else — early return on the failing branch

rust src/main.rs · let-else (Rust 1.65+)
fn first_word(s: &str) -> &str {
    let Some(pos) = s.find(' ') else {
        return s;  // no space — return whole string
    };
    &s[..pos]
}

fn main() {
    println!("[{}]", first_word("hello world"));
    println!("[{}]", first_word("single"));
}

5 · Common mistakes

  • Forgetting exhaustiveness with _. If you don't want to handle every variant, add _ => — but think first whether you should.
  • Using if where match reads cleaner. An if let Some(x) = opt { ... } else { ... } chain is often clearer as a full match.
  • Forgetting ref in patterns that need a reference. When destructuring without consuming, use ref: let User { ref name, age } = u;.

6 · When it clicks

  • You destructure tuples, structs, and enums in let without thinking.
  • Match guards (n if n < 0) feel native, not advanced.
  • You reach for let ... else for early-exit on optional values.
Found this useful?