switch Expressions
A switch chooses one branch from several alternatives.
const name = switch (code) {
200 => "ok",
404 => "not found",
500 => "server error",
else => "unknown",
};This is an expression. The value of the selected branch becomes the value of name.
The value being tested is called the switch operand.
switch (code) {
...
}Each branch has one or more cases.
200 => "ok",The value before => is matched against the operand. The expression after => is evaluated if the case matches.
Several cases can share one branch.
const kind = switch (ch) {
'a', 'e', 'i', 'o', 'u' => "vowel",
else => "other",
};Ranges are allowed for integer and character values.
const class = switch (ch) {
'0'...'9' => "digit",
'a'...'z' => "lowercase",
'A'...'Z' => "uppercase",
else => "other",
};The range includes both ends. The case '0'...'9' includes '0' and '9'.
A switch must be exhaustive. Every possible value must be handled.
For integers, this usually means using else.
const sign = switch (n) {
0 => "zero",
else => "nonzero",
};For enums, else is often unnecessary because the compiler knows all cases.
const Color = enum {
red,
green,
blue,
};
const text = switch (color) {
.red => "red",
.green => "green",
.blue => "blue",
};If a new enum value is added later, the compiler can point to every switch that must be updated. This is one of the main reasons to prefer a full enum switch over an else branch.
Branches may use blocks.
const price = switch (kind) {
.small => 10,
.large => blk: {
const base = 20;
const tax = 2;
break :blk base + tax;
},
};The block computes the branch value and gives it back with break.
All branches must produce compatible types when the switch value is used.
const x = switch (n) {
0 => 10,
1 => 20,
else => 30,
};This is valid. Every branch gives an integer.
This is wrong:
const x = switch (n) {
0 => 10,
1 => "twenty",
else => 30,
};One branch gives a string. The others give integers. Zig rejects the program.
A switch can also be used only for control flow.
switch (mode) {
.debug => std.debug.print("debug\n", .{}),
.release => std.debug.print("release\n", .{}),
}In this case each branch has type void.
switch works well with tagged unions. A tagged union stores one value chosen from a set of named alternatives.
const Token = union(enum) {
number: i64,
plus,
minus,
};
fn printToken(tok: Token) void {
switch (tok) {
.number => |n| std.debug.print("number {d}\n", .{n}),
.plus => std.debug.print("plus\n", .{}),
.minus => std.debug.print("minus\n", .{}),
}
}The branch
.number => |n| ...matches the number case and captures its payload in n.
This is safer than storing a tag and a value separately. The compiler keeps the tag and payload connected.
A switch can also capture the matched value.
const text = switch (n) {
0 => "zero",
1, 2, 3 => |x| blk: {
_ = x;
break :blk "small";
},
else => "large",
};The captured value is the value that matched the case.
Use switch when the shape of the decision matters. Use if when the decision is just a boolean test.
const result = if (n < 10) "small" else "large";This is better as if.
const result = switch (status) {
.open => "open",
.closed => "closed",
.unknown => "unknown",
};This is better as switch.
Exercise 3-9. Write a switch that converts digits 0 through 9 to their English names.
Exercise 3-10. Write a switch over an enum named Direction with cases north, south, east, and west.
Exercise 3-11. Add a new enum case and observe which switches must change.
Exercise 3-12. Write a tagged union for a simple token: integer, left parenthesis, right parenthesis, plus, and minus. Use switch to print each token.