Expressions
match — type, value and range dispatch
Select the first arm whose pattern matches a value, a type or a range, either as an expression that yields a value or as a statement.
since 0.1.0-alpha.1linuxwindowswasm
Description
match (subject) {
pattern => body;
pattern => { block }
else => body;
}
A match compares subject with each arm in source order and runs the first arm that matches; later arms are not examined. A pattern is one of:
- a type (
Dog =>): matches when the subject is an instance of that type, and narrows the subject to that type inside the arm; - a value (
0 =>,Color::Red =>): matches when the subject equals the value; - a range (
1..9 =>): matches when the subject lies between the two endpoints, both endpoints included; else: matches anything that reached it.
An arm body is either an expression followed by ; or a block in braces. match is an expression when its value is used (the arms then yield the value) and a statement when it stands alone. A match statement needs no trailing ; after its closing brace.
match is a reserved word.
Type arms, value arms and range arms
class Animal { }
class Dog : Animal { }
class Cat : Animal { }
string describe(Animal a) {
return match (a) {
Dog => "a dog";
Cat => "a cat";
else => "some other animal";
};
}
string size(int n) {
return match (n) {
0 => "zero";
1..9 => "small";
10..99 => "medium";
else => "large";
};
}
console.writeln(describe(Dog()));
console.writeln(describe(Animal()));
console.writeln(size(0));
console.writeln(size(9));
console.writeln(size(10));
console.writeln(size(-4));
a dog
some other animal
zero
small
medium
large
Rules
- Arms are tried in source order and the first match wins. When two arms overlap, the earlier one decides: with
90..100before0..100, the value95takes the first arm. - A
matchmust be exhaustive. It is complete when it ends in anelsearm, when its subject has a closed union type and every member of the union has an arm, or when its subject is an enum and every member of the enum has an arm. Otherwise the compiler reportsmatch must be exhaustive: add an 'else' arm. An open class hierarchy always needs anelsearm. - A type arm narrows the subject: inside the arm the subject has the arm's type, so its members can be used directly.
- A range arm
lo..hi =>matches whensubject >= lo && subject <= hi. Both endpoints are included, and an arm whose first endpoint is larger than its second (5.0..0.0) matches nothing. - Range arms work for every subject that the comparison operators order: all the integer widths, the float types,
string(compared lexicographically), and acharcompared against integer endpoints. The endpoints and the subject need not share a type: anintendpoint against afloatsubject promotes like>=does, somatch (2.5) { 0..5 => ... }matches. - Range arms use the ordering of the comparison operators, so a
NaNsubject matches no range arm and falls toelse,-0.0counts as equal to0.0, and an infinity lies outside every finite range. - A name written with
::in an arm is resolved by what it names: a type qualified by its namespace is a type pattern, while an enum member or a namespace constant is a value pattern.
Examples
A match over a union, narrowing in each arm
string show(int | string v) {
return match (v) {
int => "int ${v + 1}";
string => "string of length ${v.length()}";
};
}
console.writeln(show(41));
console.writeln(show("hello"));
int 42
string of length 5
Range arms on floats, strings and chars
string temperature(float t) {
return match (t) {
0.0..15.0 => "cold";
15.0..30.0 => "mild";
else => "out of range";
};
}
string half(string s) {
return match (s) {
'a'..'m' => "first half";
else => "second half";
};
}
string kind(char c) {
return match (c) {
48..57 => "digit";
97..122 => "lowercase letter";
else => "other";
};
}
console.writeln(temperature(2.5));
console.writeln(temperature(15.0));
console.writeln(temperature(99.0));
console.writeln(half("cat"));
console.writeln(half("zebra"));
char q = 'q';
console.writeln(kind(q));
char seven = '7';
console.writeln(kind(seven));
cold
cold
out of range
first half
second half
lowercase letter
digit
Enum members, namespace constants and the statement form
enum Color { Red, Green, Blue }
namespace Limits { const int Max = 3; }
string name(Color c) {
return match (c) {
Color::Red => "red";
Color::Green => "green";
Color::Blue => "blue";
};
}
string check(int n) {
return match (n) {
Limits::Max => "at the limit";
else => "below or above";
};
}
console.writeln(name(Color::Green));
console.writeln(check(3));
match (3) {
1 => console.writeln("one");
3 => { console.writeln("three"); }
else => console.writeln("other");
}
green
at the limit
three
int n = 7;
match (n) {
1 => console.writeln("one");
2 => console.writeln("two");
}
// error: match must be exhaustive: add an 'else' arm
Notes
- A
charsubject compared againstcharendpoints, as in'a'..'z', does not match anything. Write the endpoints as integer code points (97..122) instead, as thekindexample above does. Astringsubject with string endpoints is unaffected. - A range arm's endpoints are not checked against the subject's type. An arm whose subject has no ordering, such as an array or a
boolendpoint, compiles but behaves differently from one engine to another, so keep range arms to the subjects listed in the rules.
See also
- Exception — The base class of the standard exceptions, holding a message.
- Exceptions: throw, try and catch — Throwing a value that implements IException, catching it by type, and the standard exception hierarchy.