Patterns

Patterns are used in match expressions, let bindings, and function parameters to destructure values.

Literal Patterns

match x {
    0 => "zero",
    1 => "one",
    _ => "other",
}

Identifier Patterns

Bind the matched value to a name:

match value {
    x => println!("Got: {}", x),
}

Wildcard Pattern

_ matches any value and discards it:

match pair {
    (x, _) => println!("First: {}", x),
}

Enum Variant Patterns

Tuple Variants

match option {
    Option::Some(value) => use_value(value),
    Option::None => handle_empty(),
}

Named Field Variants

match shape {
    Shape::Circle { radius } => 3.14159 * radius * radius,
    Shape::Rectangle { width, height } => width * height,
    Shape::Point => 0.0,
}

Nested Patterns

match result {
    Result::Ok(Option::Some(value)) => use_value(value),
    Result::Ok(Option::None) => handle_none(),
    Result::Err(e) => handle_error(e),
}

Struct Patterns

let Point { x, y } = point;

In match:

match token {
    Token { kind: TokenKind::Fn, span } => parse_function(span),
    Token { kind: TokenKind::Struct, span } => parse_struct(span),
    _ => parse_expression(),
}

Tuple Patterns

let (a, b) = (1, 2);

match pair {
    (0, 0) => "origin",
    (x, 0) => "x-axis",
    (0, y) => "y-axis",
    (x, y) => "other",
}

Reference Patterns

match &value {
    &Some(x) => use_value(x),
    &None => handle_none(),
}

Or Patterns

Match multiple alternatives in a single arm using |:

match x {
    1 | 2 | 3 => "small",
    4 | 5 | 6 => "medium",
    _ => "large",
}

Or patterns work with enum variants:

match direction {
    Direction::North | Direction::South => "vertical",
    Direction::East | Direction::West => "horizontal",
}

They also work with nested patterns:

match result {
    Result::Ok(1 | 2 | 3) => "small success",
    Result::Ok(_) => "other success",
    Result::Err(_) => "failure",
}

Range Patterns

Match a contiguous range of values using ..= (inclusive):

match score {
    0..=59 => "F",
    60..=69 => "D",
    70..=79 => "C",
    80..=89 => "B",
    90..=100 => "A",
    _ => "invalid",
}

Range patterns work with integer types:

match byte {
    0x00..=0x1F => "control character",
    0x20..=0x7E => "printable ASCII",
    0x7F => "delete",
    _ => "extended",
}

And with characters:

match c {
    'a'..='z' => "lowercase",
    'A'..='Z' => "uppercase",
    '0'..='9' => "digit",
    _ => "other",
}

Guard Clauses

Add a boolean condition to a match arm with if:

match value {
    x if x > 100 => "large",
    x if x > 0 => "positive",
    x if x < 0 => "negative",
    _ => "zero",
}

Guards can reference variables bound in the pattern:

match point {
    Point { x, y } if x == y => "on diagonal",
    Point { x, y } if x == 0 => "on y-axis",
    Point { x, y } if y == 0 => "on x-axis",
    _ => "general",
}

Guards combine with or patterns:

match value {
    1 | 2 | 3 if verbose => {
        println!("small value: {}", value);
        "small"
    }
    _ => "other",
}

Guard Evaluation

  • The guard expression is evaluated only if the structural pattern matches
  • Guards do not affect exhaustiveness checking -- the compiler still requires all variants to be covered
  • If the guard evaluates to false, matching continues to the next arm

Exhaustiveness

The compiler verifies that match expressions cover all possible cases. Omitting a variant produces a compile-time error:

error: non-exhaustive match
  --> program.joule:10:5
   |
10 |     match color {
   |     ^^^^^ missing variants: Blue

Use _ as a catch-all when you don't need to handle every variant explicitly.