๐Ÿฆ€ Functional Rust
๐ŸŽฌ Pattern Matching Exhaustive match, destructuring, guards, let-else โ€” compiler-verified branching.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข match is exhaustive โ€” the compiler ensures every variant is handled

โ€ข Destructuring extracts fields from structs, tuples, and enums in one step

โ€ข Guards (if conditions) add extra logic to match arms

โ€ข if let and let-else provide concise single-pattern matching

โ€ข Nested patterns and @ bindings handle complex data shapes elegantly

568: @ Bindings in Patterns

Difficulty: 2 Level: Beginner Match a pattern AND bind the matched value to a name โ€” get both the test and the variable.

The Problem This Solves

Here's the conflict: you want to match a range of values, but you also need the actual value inside the arm. Without `@`, you pick one or the other. You can write `1..=100 => "small"` and lose the value. You can write `x => if x >= 1 && x <= 100 { ... }` and lose the clean pattern syntax. You can't have both in the same arm. The same problem appears with enum variants: you want to fire a log message that includes the whole `Event` struct, but also destructure its fields for the actual logic. Without `@`, you'd either bind the whole thing and access fields through `.x`, `.y`, or destructure and lose the original. `@` bindings solve this: `n @ 1..=100` matches the range AND binds `n` to the actual value. `e @ Event::Click { x, y }` binds the whole event to `e` AND gives you `x` and `y` from the destructure.

The Intuition

The `@` symbol reads as "at" โ€” "bind the value at this position to this name, where the value also matches this pattern." It's glue between a name and a condition. OCaml spells this `as`: `Click(x, y) as e when x > 0` โ€” the matched value is available as `e`, and `x`, `y` are destructured. Rust uses `@` instead. The semantics are identical. The practical rule: reach for `@` when you need to log, debug, forward, or compare the whole matched thing, while also branching on its shape or value. `n @ 0 => ("zero", n)` โ€” a zero cost way to have your cake and eat it too.

How It Works in Rust

// @ with range โ€” bind the value AND test the range
fn categorize(n: i32) -> (&'static str, i32) {
 match n {
     x @ 0           => ("zero",           x),
     x @ 1..=100     => ("small positive", x),
     x @ 101..=1000  => ("medium",         x),
     x               => ("large",          x),  // catch-all, still bound
 }
}

// @ with enum + guard โ€” bind whole event AND destructure fields
fn handle(ev: &Event) -> String {
 match ev {
     // e is the whole Event, x and y are destructured from it
     e @ Event::Click { x, y } if *x > 0 && *y > 0 =>
         format!("valid click {:?}", e),

     Event::Click { .. } => "invalid click".into(),

     // @ with character range โ€” bind AND test
     Event::Key(c @ 'a'..='z') => format!("lower: {}", c),
     Event::Key(c @ 'A'..='Z') => format!("upper: {}", c),
     Event::Key(c)             => format!("other: {}", c),

     Event::Resize(w, h) => format!("resize {}x{}", w, h),
 }
}

What This Unlocks

Key Differences

ConceptOCamlRust
Syntax`pattern as name``name @ pattern`
With guard`Click(x,y) as e when x > 0``e @ Event::Click{x,y} if *x > 0`
Bind + rangeNo native range patterns`n @ 1..=100`
Bind + destructure`(Some x) as opt``opt @ Some(x)`
PositionName comes after `as`Name comes before `@`
#[derive(Debug)]
enum Event { Click{x:i32,y:i32}, Key(char), Resize(u32,u32) }

fn handle(ev: &Event) -> String {
    match ev {
        e @ Event::Click{x,y} if *x>0 && *y>0 => format!("valid click {:?}", e),
        Event::Click{..}                        => "invalid click".into(),
        Event::Key(c @ 'a'..='z')              => format!("lower: {}", c),
        Event::Key(c @ 'A'..='Z')              => format!("upper: {}", c),
        Event::Key(c)                           => format!("other: {}", c),
        Event::Resize(w,h)                      => format!("resize {}x{}", w, h),
    }
}

fn categorize(n: i32) -> (&'static str, i32) {
    match n {
        x @ 0           => ("zero",           x),
        x @ 1..=100     => ("small positive", x),
        x @ 101..=1000  => ("medium",         x),
        x               => ("large",          x),
    }
}

fn main() {
    for ev in [Event::Click{x:10,y:20}, Event::Click{x:-1,y:5},
               Event::Key('a'), Event::Key('Z'), Event::Resize(800,600)] {
        println!("{}", handle(&ev));
    }
    for n in [0,50,500,5000] {
        let (cat,v) = categorize(n); println!("{}: {}", cat, v);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test] fn cat_zero()  { assert_eq!(categorize(0).0, "zero"); }
    #[test] fn cat_small() { assert_eq!(categorize(42).0, "small positive"); }
    #[test] fn valid_click() { assert!(handle(&Event::Click{x:1,y:1}).contains("valid")); }
}
(* @ (as) bindings in OCaml *)
type event = Click of int*int | Key of char | Resize of int*int

let handle = function
  | Click(x,y) as e when x>0 && y>0 ->
    Printf.printf "valid click at (%d,%d): %s\n" x y (match e with Click _->"click"|_->"?")
  | Key c when c >= 'a' && c <= 'z' -> Printf.printf "lower: %c\n" c
  | Key c when c >= 'A' && c <= 'Z' -> Printf.printf "upper: %c\n" c
  | Key c  -> Printf.printf "other key: %c\n" c
  | Resize(w,h) -> Printf.printf "resize %dx%d\n" w h
  | _ -> ()

let () =
  List.iter handle [Click(10,20); Click(-1,5); Key 'a'; Key 'Z'; Resize(800,600)]