Match Expressions
Match expressions provide pattern matching for clean, exhaustive branching.
Basic Syntax
match value {
pattern1 => result1,
pattern2 => result2,
_ => default_result
}The _ is a wildcard that matches any value not handled by previous arms.
Matching Integers
function describe(x: i32) i32 {
return match x {
1 => 100,
2 => 200,
3 => 300,
_ => 0
};
}
function main() i32 {
return describe(2); // Returns 200
}Match as Expression
Match is an expression, so it returns a value that can be used directly:
function main() i32 {
var x = 2;
var result = match x {
1 => 10,
2 => 20,
_ => 0
};
return result; // 20
}You can use match anywhere an expression is expected:
function main() i32 {
var x = 1;
var y = 10 + match x {
1 => 5,
_ => 0
};
return y; // 15
}Block Bodies
Use braces for multi-statement arm bodies:
function main() i32 {
var x = 2;
match x {
1 => {
var a = 10;
return a + 5;
},
2 => {
var b = 20;
return b + 5;
},
_ => {
return 0;
}
};
return 0;
}Matching Booleans
function main() i32 {
var flag = true;
return match flag {
true => 1,
false => 0,
_ => 99
};
}Matching Enums
Match works naturally with enum types:
enum Color { Red, Green, Blue }
function to_code(c: Color) i32 {
return match c {
Color.Red => 1,
Color.Green => 2,
Color.Blue => 3,
_ => 0
};
}
function main() i32 {
var c: Color = Color.Green;
return to_code(c); // 2
}Match as Statement
Use match as a statement for side effects:
function main() i32 {
var x = 2;
var result = 0;
match x {
1 => { result = 10; },
2 => { result = 20; },
_ => { result = 99; }
};
return result; // 20
}Return from Match Arms
You can return from the enclosing function inside a match arm:
function process(x: i32) i32 {
match x {
1 => { return 10; },
2 => { return 20; },
_ => { return 99; }
};
return 0; // Never reached if all cases return
}Nested Match
Match expressions can be nested:
function main() i32 {
var x = 1;
var y = 2;
return match x {
1 => match y {
1 => 11,
2 => 12,
_ => 10
},
2 => 20,
_ => 0
}; // Returns 12
}No Match Fallthrough
If no arm matches and there's no wildcard _, execution continues after the match:
function main() i32 {
var x = 99;
match x {
1 => { return 10; },
2 => { return 20; }
};
return 0; // Reached when x is neither 1 nor 2
}Without a wildcard arm, unmatched values will fall through silently. Consider always including a _ arm for safety.
Trailing Commas
Trailing commas are allowed:
match x {
1 => 10,
2 => 20,
_ => 0,
}Different Integer Sizes
Match handles different integer sizes (i32, i64, etc.):
function main() i32 {
var x: i64 = 2;
var result: i64 = match x {
1 => 100,
2 => 200,
_ => 0
};
return result; // 200
}Destructuring Enum Payloads
Matching a payload-carrying enum destructures the payload into fresh
bindings; _ skips a position:
enum Shape { Circle(f64), Rect(f64, f64), Empty }
function describe(s: ref Shape) f64 {
return match s {
Shape.Circle(r) => r,
Shape.Rect(w, _) => w,
Shape.Empty => 0.0
};
}A match borrows payloads in place when no reachable arm takes ownership of
one, so inspection does not consume the enum or require an explicit reference.
If any reachable arm moves a compound payload, the match consumes the owned
enum: the selected arm owns its bindings and drops remaining payloads at arm
exit. A match result stays alive until its receiving owner releases it.
Matching a ref or const ref enum always borrows and rejects payload moves.
See Ownership and Drops.
A non-enum match producing an owned value needs a wildcard, or both boolean patterns, so every input produces a valid result.
Only the first matching arm runs; there is no fallthrough. A wildcard handles values not covered by preceding arms.
Enum matches are exhaustive: cover every variant or add a _ arm. Duplicate
arms and arms after _ produce unreachable-arm warnings.
Ordinary value patterns compare for equality. Guards, range patterns, and nested destructuring are planned for future versions.
Typed borrowed bindings
A typed pattern uses the same name: type spelling as a function parameter:
match error {
(e: ref RetryableError) => { /* Handle a retryable payload. */ },
(_: ref IError) => { /* Handle other error payloads. */ },
_ => { /* Handle remaining variants. */ }
};Use const ref for read-only access. _ in a typed binding keeps the type test
without creating a variable; a plain _ matches unconditionally.
On an enum, a typed pattern examines each single-payload variant. A concrete class pattern requires that exact payload type; an interface pattern accepts payload classes implementing the interface or borrowed interfaces extending it. Matching a concrete class directly is also supported. Interface patterns do not perform runtime downcasts of an interface value.
Arms run in source order. Exhaustiveness checking includes every variant covered by a typed pattern. Multiple variants carrying the same concrete type are grouped by a typed pattern; an ordinary variant pattern can distinguish them.
Bindings borrow the active payload. They neither move nor copy it, and they
cannot outlive its owner. Mutable bindings cannot borrow through a const value.
Payload-free and multiple-payload variants use ordinary variant patterns or _.
Nested enums require another match: matching _Result<T, ErrorEnum> against an
interface does not recursively inspect the variants inside ErrorEnum.