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

569: Range Patterns: 1..=10

Difficulty: 2 Level: Beginner Match integers and characters against inclusive ranges โ€” replace branching chains with clean interval dispatch.

The Problem This Solves

Tax brackets. Grade cutoffs. Character classification. Age groups. These all share the same shape: a numeric value falls into one of several non-overlapping ranges, and each range gets different treatment. Without range patterns, you write an if/else chain: `if score >= 90 { 'A' } else if score >= 80 { 'B' } ...`. That works, but it's easy to introduce gaps or overlaps โ€” a value of exactly 80 could be unintentionally handled by the wrong branch if you flip a comparison. The deeper issue is readability: the intent is "90 through 100 maps to A." The code says "greater than or equal to 90, and we'll check the upper bound implicitly because of arm ordering." Range patterns make the bounds explicit and the match compiler checks for gaps.

The Intuition

Range patterns in `match` work exactly like ranges in everyday math notation, just spelled with `..=`. `90..=100` means "every integer from 90 to 100, inclusive." The `..=` is always inclusive in patterns โ€” there is no exclusive range pattern. OCaml doesn't have native range patterns; it simulates them with guards (`when n >= 90`). Guards are fine but miss one thing: the compiler can't prove exhaustiveness with guards. With Rust range patterns, the compiler can often verify coverage. Write `i32::MIN..=-1 | 0 | 1..=i32::MAX` and Rust knows you've covered every integer. Range patterns also work on `char`, which is a clean win for character classification โ€” `'a'..='z'`, `'A'..='Z'`, `'0'..='9'` cover exactly the right characters.

How It Works in Rust

// Grade bands โ€” inclusive on both ends
fn grade(score: u8) -> char {
 match score {
     90..=100 => 'A',
     80..=89  => 'B',
     70..=79  => 'C',
     60..=69  => 'D',
     _        => 'F',   // 0..=59
 }
}

// char ranges โ€” works for any Unicode scalar value
fn classify_char(c: char) -> &'static str {
 match c {
     'A'..='Z' => "upper",
     'a'..='z' => "lower",
     '0'..='9' => "digit",
     _         => "other",
 }
}

// Tax brackets โ€” use underscores for readability
fn tax_rate(income: u32) -> f64 {
 match income {
     0..=10_000          => 0.10,
     10_001..=40_000     => 0.12,
     40_001..=85_000     => 0.22,
     85_001..=163_300    => 0.24,
     _                   => 0.32,
 }
}

// Full coverage without _ โ€” compiler verifies completeness
fn sign(n: i32) -> &'static str {
 match n {
     i32::MIN..=-1 => "negative",
     0             => "zero",
     1..=i32::MAX  => "positive",
 }
}

What This Unlocks

Key Differences

ConceptOCamlRust
SyntaxGuards: `when n >= 90 && n <= 100``90..=100` pattern
Char range`when Char.code c >= 65 && ...``'A'..='Z'`
ExhaustivenessCompiler can't verify guard rangesCan verify with full range coverage
Exclusive rangeN/A`..` (not valid in patterns โ€” only `..=`)
ReadabilityVerbose numeric comparisonsLiteral ranges matching intent
fn grade(score: u8) -> char {
    match score {
        90..=100 => 'A',
        80..=89  => 'B',
        70..=79  => 'C',
        60..=69  => 'D',
        _        => 'F',
    }
}

fn classify_char(c: char) -> &'static str {
    match c {
        'A'..='Z' => "upper",
        'a'..='z' => "lower",
        '0'..='9' => "digit",
        _         => "other",
    }
}

fn tax(income: u32) -> f64 {
    match income {
        0..=10_000          => 0.10,
        10_001..=40_000     => 0.12,
        40_001..=85_000     => 0.22,
        85_001..=163_300    => 0.24,
        _                   => 0.32,
    }
}

fn main() {
    for s in [95u8,82,74,61,45] { print!("{}->{} ", s, grade(s)); } println!();
    for c in ['A','z','5','!']   { print!("{}:{} ", c, classify_char(c)); } println!();
    for i in [5_000u32, 25_000, 60_000, 100_000] {
        println!("income {} -> {:.0}% tax", i, tax(i)*100.0);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test] fn test_grade()      { assert_eq!(grade(95),'A'); assert_eq!(grade(55),'F'); }
    #[test] fn test_char_lower() { assert_eq!(classify_char('x'), "lower"); }
    #[test] fn test_tax()        { assert_eq!(tax(5_000), 0.10); }
}
(* Range-like matching in OCaml via guards *)
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'

let classify_char c =
  match Char.code c with
  | n when n >= 65 && n <= 90  -> "upper"
  | n when n >= 97 && n <= 122 -> "lower"
  | n when n >= 48 && n <= 57  -> "digit"
  | _                          -> "other"

let () =
  List.iter (fun n -> Printf.printf "%d->%c " n (grade n)) [95;82;74;61;45];
  print_newline ();
  List.iter (fun c -> Printf.printf "%c:%s " c (classify_char c)) ['A';'z';'5';'!']