Skip to content
CryoCryo home
LanguageControl and data

7Pattern Matching

A pattern describes the shape of a value. When a value matches, any variables in the pattern are bound to the corresponding parts.

7.1 Pattern Forms

PatternSyntaxMatches
Literal42, "hello", true, 'A'Exactly that value.
IdentifierxAny value; binds it to x.
Wildcard_Any value; discards it.
Enum (unit)Color::RedThat variant, no payload.
Enum (with payload)Shape::Circle(r)That variant; binds the payload to r.
Range'0'..'9'Any value in the range (inclusive).
Orpat | pat | patAny of the listed patterns.

7.2 Enum Destructuring

type enum Shape {
    Circle(f64);
    Rectangle(f64, f64);
    Point;
}

function describe(s: Shape) -> void {
    match (s) {
        Shape::Circle(r)        => { printf("Circle r=%f\n", r); }
        Shape::Rectangle(w, h)  => { printf("Rectangle %f x %f\n", w, h); }
        Shape::Point            => { println("A point"); }
    }
}

In each arm, the variables are introduced for the payload of that variant. The compiler enforces that the count and types match the variant's declaration.

If you don't need a payload, use _: Option::Some(_) => { ... }.

7.3 Range Patterns

Range patterns match values within an inclusive range. They are most useful for character classification. Both spellings - a..b and the explicit a..=b - are inclusive in pattern position (note this differs from a range expression, where a..b is half-open). Bounds must be integer or char literals of the same kind.

match (ch) {
    '0'..='9'                   => { println("digit"); }
    'a'..'z' | 'A'..'Z' | '_'   => { println("ident-start"); }
    _                           => { println("other"); }
}

7.4 Guard Clauses

An arm may carry a guard: a boolean condition written if (cond) between the pattern and the =>. The guard is evaluated only after the pattern matches; if it is false, matching falls through to the next arm. Any bindings introduced by the pattern are in scope inside the guard.

match (n) {
    x if (x > 100) => { 3 }
    x if (x > 10)  => { 2 }
    x if (x > 0)   => { 1 }
    _              => { 0 }
}

match (o) {
    Option::Some(v) if (v > 5) => { v * 10; }
    Option::Some(v)            => { v; }
    Option::None               => { -1; }
}

The parentheses around the condition are required. A guarded arm does not count toward exhaustiveness (the guard could always be false), so a match whose only arm for some case is guarded still needs a fall-through arm.

7.5 Exhaustiveness

The compiler checks that every possible value of the matched type is covered. Forgetting a variant of an enum is an error. The wildcard _ is the explicit way to opt in to a default arm.