Patterns destructure values and bind variables in a single step.
match tests a value against patterns and runs the first matching arm:
fn describe(n: int) -> string {
match n {
0 => "zero",
1 => "one",
_ => "many",
}
}Each arm has a pattern, =>, and an expression. The entire match is an expression that produces the value of the matched arm.
Integer, boolean, string, and character literals match exact values:
match c {
'a' => "letter a",
'b' => "letter b",
_ => "other",
}An identifier binds the matched value to a name:
match opt {
Some(x) => x * 2,
None => 0,
}_ matches any value without binding:
match opt {
Some(_) => "has value",
None => "empty",
}Tuple patterns destructure by position:
let pair = (10, 20)
match pair {
(0, 0) => "origin",
(x, 0) => f"on x-axis at {x}",
(0, y) => f"on y-axis at {y}",
(x, y) => f"at ({x}, {y})",
}Struct patterns match fields by name:
struct Point { x: int, y: int }
match p {
Point { x: 0, y: 0 } => "origin",
Point { x, y: 0 } => f"on x-axis at {x}",
Point { x, y } => f"at ({x}, {y})",
}Use .. to ignore remaining fields:
struct User { name: string, email: string, age: int }
match user {
User { name, .. } => f"hello, {name}",
}Enum patterns match variants and destructure their payloads:
enum Message {
Ready,
Write(string),
Move { x: int, y: int },
}
match msg {
Message.Ready => "ready",
Message.Write(text) => f"writing: {text}",
Message.Move { x, y } => f"moving to ({x}, {y})",
}Inside a match arm, the enum qualifier can be omitted:
match msg {
Ready => "ready",
Write(text) => f"writing: {text}",
Move { x, y } => f"moving to ({x}, {y})",
}Slice patterns match elements:
match items {
[] => "empty",
[x] => f"single: {x}",
[first, second] => f"pair: {first}, {second}",
[first, ..rest] => f"first is {first}, {rest.length()} more",
}The rest pattern ..rest captures remaining elements as a slice. It must appear last; elements after .. are not allowed.
Use .. without an identifier to ignore the rest:
match items {
[first, ..] => first,
[] => 0,
}Use | to match multiple patterns in one arm:
match dir {
Direction.North | Direction.South => "vertical",
Direction.East | Direction.West => "horizontal",
}Or-patterns can bind variables if all alternatives bind the same names:
enum Event {
KeyDown(rune),
KeyUp(rune),
}
match event {
Event.KeyDown(c) | Event.KeyUp(c) => f"key: {c}",
}Or-patterns can only appear at the top level of a match arm. They cannot be nested inside other patterns.
Use as to capture the matched value:
match msg {
Message.Move { x, .. } as m => log.insert(x, m),
_ => {},
}Captured values are available in guards:
match opt {
Some(Point { x, .. }) as p if x > 0 => transform(p),
_ => default,
}For or-patterns, place as on each alternative:
match event {
Event.KeyDown(c) as e | Event.KeyUp(c) as e => record(e, c),
}as is only allowed in match, if let, and while let.
Add if after a pattern to require an additional condition:
match opt {
Some(x) if x > 0 => "positive",
Some(_) => "non-positive",
None => "empty",
}Guards do not count toward exhaustiveness. If all arms have guards, a wildcard or catch-all arm is still required.
Patterns must cover all possible values:
enum Color { Red, Green, Blue }
match color {
Color.Red => "red",
Color.Green => "green",
}The compiler enforces exhaustiveness:
[error] Non-exhaustive match
╭─[example.lis:3:1]
3 │ match color {
· ───────┬───
· ╰── not all patterns covered
4 │ Color.Red => "red",
5 │ Color.Green => "green",
6 │ }
╰────
help: Handle the missing case `Color.Blue`, e.g. `Color.Blue => { ... }`
Patterns can destructure values in let bindings:
let (x, y) = get_point()
let Point { x, y } = p
let [first, ..rest] = items else { return }Slice patterns are refutable because Slice<T> has unknown length — use let else to handle the non-matching case.
let else either matches or leads to an early exit:
fn process(opt: Option<int>) -> int {
let Some(x) = opt else { return 0; };
x * 2
}The else block must return, break, or continue.
← 07-pointers.md |
09-error-handling.md → |