๐Ÿฆ€ 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

562: Match Guards with if Conditions

Difficulty: 2 Level: Beginner A match guard adds an `if condition` after a pattern. The arm only fires if both the pattern matches and the condition is true. Guards handle cases that pattern syntax alone can't express.

The Problem This Solves

Patterns match on structure and values, but not on arbitrary predicates. A pattern can match `Shape::Circle(r)` โ€” it can't match "a Circle whose radius is between 0.1 and 1.0." That requires a guard. Without guards, you'd need nested `if` statements inside each match arm, or a helper function, losing the clean exhaustiveness checking of `match`:
match shape {
 Shape::Circle(r) => {
     if r <= 0.0 { "invalid" }
     else if r < 1.0 { "tiny" }
     else { "circle" }  // nested ifs โ€” readable but breaks the match structure
 }
 ...
}
Guards keep the branching in the `match` expression itself, where the exhaustiveness checker can see it.

The Intuition

`pattern if condition => arm` reads as "if the value looks like `pattern` AND satisfies `condition`, execute this arm." If the pattern matches but the guard is false, the match continues to the next arm โ€” like the pattern didn't match at all. Guards are evaluated after the pattern binds its variables, so you can use bound names inside the condition: `Shape::Circle(r) if *r < 1.0`. Important: A guard that doesn't cover all cases for a pattern makes that pattern non-exhaustive for the match checker's purposes. You often need a final fallback arm.

How It Works in Rust

Shape classification with guards:
#[derive(Debug)]
enum Shape { Circle(f64), Rect(f64, f64) }

fn describe(s: &Shape) -> &'static str {
 match s {
     Shape::Circle(r) if *r <= 0.0  => "invalid",   // guard: radius non-positive
     Shape::Circle(r) if *r < 1.0   => "tiny circle",
     Shape::Circle(_)                => "circle",     // catch-all for Circle
     Shape::Rect(w, h) if w == h     => "square",    // equal sides
     Shape::Rect(w, h) if w > h      => "wide",
     Shape::Rect(_, _)               => "tall",       // catch-all for Rect
 }
}
Grade calculation โ€” guard on a binding:
fn grade(score: u32) -> char {
 match score {
     n if n >= 90 => 'A',
     n if n >= 80 => 'B',
     n if n >= 70 => 'C',
     n if n >= 60 => 'D',
     _            => 'F',
 }
}
// Note: guards are checked top-to-bottom โ€” order matters
// n >= 90 is checked first, so 95 correctly gets 'A', not 'B'
Guards with or-patterns:
// Guard applies to BOTH alternatives in an or-pattern
fn classify_temp(celsius: f64) -> &'static str {
 match celsius as i32 {
     n if n < 0         => "freezing",
     0 | 1 | 2 | 3 if true => "near zero",  // or: use range
     n if n < 20        => "cold",
     n if n < 30        => "warm",
     _                  => "hot",
 }
}
Guards with struct patterns:
struct Point { x: i32, y: i32 }

fn quadrant(p: &Point) -> &'static str {
 match p {
     Point { x, y } if *x > 0 && *y > 0 => "Q1",
     Point { x, y } if *x < 0 && *y > 0 => "Q2",
     Point { x, y } if *x < 0 && *y < 0 => "Q3",
     Point { x, y } if *x > 0 && *y < 0 => "Q4",
     _                                   => "on axis",
 }
}

What This Unlocks

Key Differences

ConceptOCamlRust
Match guard syntax`pattern when condition ->``pattern if condition =>`
Guard variable accessBound names availableSame โ€” bound names usable in the guard condition
Guard + or-pattern`(p1 \p2) when cond ->``p1 \p2 if cond =>` โ€” guard applies to both
ExhaustivenessGuards make arms non-exhaustive; compiler warnsSame โ€” a guarded arm doesn't cover its pattern fully
Guard evaluation orderTop-to-bottom; first match winsSame โ€” ordered evaluation, first matching arm + guard wins
#[derive(Debug)]
enum Shape { Circle(f64), Rect(f64, f64) }

fn describe(s: &Shape) -> &'static str {
    match s {
        Shape::Circle(r) if *r <= 0.0   => "invalid",
        Shape::Circle(r) if *r < 1.0    => "tiny circle",
        Shape::Circle(_)                 => "circle",
        Shape::Rect(w, h) if w == h      => "square",
        Shape::Rect(w, h) if w > h       => "wide",
        Shape::Rect(_, _)                => "tall",
    }
}

fn grade(score: u32) -> char {
    match score {
        n if n >= 90 => 'A',
        n if n >= 80 => 'B',
        n if n >= 70 => 'C',
        n if n >= 60 => 'D',
        _            => 'F',
    }
}

fn main() {
    for s in [Shape::Circle(-1.0), Shape::Circle(0.5), Shape::Circle(2.0),
              Shape::Rect(3.0,3.0), Shape::Rect(5.0,2.0), Shape::Rect(2.0,5.0)] {
        println!("{:?} -> {}", s, describe(&s));
    }
    for n in [95u32, 82, 74, 61, 45] {
        println!("{} -> {}", n, grade(n));
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test] fn square() { assert_eq!(describe(&Shape::Rect(4.0,4.0)), "square"); }
    #[test] fn grade_a() { assert_eq!(grade(95), 'A'); }
    #[test] fn grade_f() { assert_eq!(grade(55), 'F'); }
}
(* Match guards with 'when' in OCaml *)
let grade n =
  match n with
  | n when n >= 90 -> "A"
  | n when n >= 80 -> "B"
  | n when n >= 70 -> "C"
  | n when n >= 60 -> "D"
  | _              -> "F"

type shape = Circle of float | Rect of float * float

let describe = function
  | Circle r when r <= 0.0 -> "invalid"
  | Circle r when r < 1.0  -> "tiny circle"
  | Circle _               -> "circle"
  | Rect (w, h) when w = h -> "square"
  | Rect (w, h) when w > h -> "wide"
  | Rect _                 -> "tall"

let () =
  List.iter (fun n -> Printf.printf "%d->%s " n (grade n)) [95;82;74;61;45];
  print_newline ();
  List.iter (fun s -> Printf.printf "%s " (describe s))
    [Circle (-1.0); Circle 0.5; Circle 2.0; Rect(3.,3.); Rect(5.,2.); Rect(2.,5.)];
  print_newline ()