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
| Pattern | Syntax | Matches |
|---|---|---|
| Literal | 42, "hello", true, 'A' | Exactly that value. |
| Identifier | x | Any value; binds it to x. |
| Wildcard | _ | Any value; discards it. |
| Enum (unit) | Color::Red | That 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). |
| Or | pat | pat | pat | Any of the listed patterns. |
A literal or range pattern is of its literal's kind, and that kind must be the subject's own type: an integer pattern matches an integer subject, a string pattern a string, a character pattern a char. A pattern of any other kind is refused at the pattern (E0200) rather than built as an arm that never matches - "x" against a struct subject is an error, not a dead arm.
7.2 Enum Destructuring
import std::fmt::{ printf, println };
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(_) => { ... }.
The alternatives of an or-pattern bind one variable per name, whichever alternative matched, so every alternative must bind the same names, each at the same type and each written mut alike; otherwise the arm is refused (E0407):
match (s) {
Shape::Circle(r) | Shape::Rectangle(r, _) => { r } // one `r`, an f64 either way
Shape::Rectangle(w, h) | Shape::Circle(w) => { w } // E0407: `h` is not bound in the second alternative
}
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.
import std::fmt::{ println };
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.
import a::b;
import a::b::{ X, Y };
import a::b as c;
import a::b::*;Brings a module, or selected items from it, into scope. One path per declaration: use the brace form for several items, as to alias, and * for everything public (prefer the brace form; wildcards invite name collisions).
function printf(fmt: string, args...) -> i32Write a printf-style formatted string to stdout. Returns the number of bytes written, or a negative value on error (libc printf convention). Use this for genuinely variadic %s/%d output; for value formatting prefer an f-string through println (Display-based) below.
type struct Name { ... }
type enum Name { ... }
type trait Name { ... }
type union Name { ... }Begins a type declaration; the keyword that follows says which kind. On its own, type Name = Existing; declares an alias.
type enum Name {
Unit,
Payload(T),
Named { x: i32 },
}An algebraic data type. Variants may be bare or carry a tuple or struct payload, and a match on an enum must cover every variant. Name::Variant names a variant; methods are added with implement enum Name { ... }.
type enum ShapeShape::Circle(f64)Shape::Rectangle(f64, f64)Shape::Pointfunction name<T>(param: Type) -> Ret { ... }Declares a function. Parameters are written name: Type and are never inferred; the return type follows -> and defaults to void. Declaration order does not matter: every signature is collected before any body is checked, so functions may call each other freely and recurse.
function describe(s: Shape) -> void(parameter) s: Shapetype enum Shapematch (subject) {
Pattern(x) => { ... }
_ => { ... }
}The primary discriminator: branches on enum variants, integers, ranges, and destructuring patterns, and the compiler rejects a match that misses a case. There is no fallthrough. As an expression it evaluates to the chosen arm's value, so every arm must produce the same type.
(parameter) s: ShapeShape::Circle(f64)r: f64pattern => { body }Separates a match arm's pattern from its body. Arms do not fall through.
r: f64Shape::Rectangle(f64, f64)w: f64h: f64w: f64h: f64Shape::Pointtype enum ShapeShape::Circle(f64)Shape::Rectangle(f64, f64)_r: f64w: f64start..=endAn inclusive range - start through end - and sugar for RangeInclusive::new(start, end). Also a match pattern: 1..=5 => { ... }.
start..endA half-open range - start up to but excluding end - and sugar for Range::new(start, end): an iterator usable in for, and an ordinary value anywhere else. It binds looser than arithmetic, so a..b + 1 is a..(b + 1).
xif (condition) { ... } else if (condition) { ... } else { ... }Branches on a parenthesised boolean condition. Bodies are always braced; there is no single-statement form. Used as an expression, both arms are required and must produce the same type: const sign = if (n < 0) { -1 } else { 1 };.
Option::Some(T)vOption::NonetrueThe boolean literal. Booleans are a distinct 1-byte type, not interchangeable with integers; if and while conditions must be boolean.
primitive stringNUL-terminated raw string (u8*), matching the C ABI. Length-typed text is Str / String.
function name(count: i32, args...)A variadic parameter: the trailing bucket receives any number of further arguments with C's calling convention, so the function stays ABI-compatible with the printf family. Wrap the bucket in VaArgs to read typed values from it.
mut name: Type = value;A mutable binding, reassignable after declaration - mutability is opt-in, const is the default. At module level it declares mutable global state. Before & it marks an exclusive reference or receiver instead.