Switch
const std = @import("std");
const expect = std.testing.expect;
test "switch as a statement" {
var x: i8 = 10;
switch (x) {
-1...1 => x = -x, // inclusive ranges
10, 100 => x = @divExact(x, 10), // multiple values
else => {},
}
try expect(x == 1);
}
test "switch as an expression" {
const x: i8 = 10;
const y: i8 = switch (x) {
-1...1 => -x,
10, 100 => @divExact(x, 10),
else => 0,
};
try expect(y == 1);
}
const Direction = enum { north, south, east, west };
fn isVertical(d: Direction) bool {
// No `else` branch: the compiler proves every case is handled, so adding
// a new enum tag later turns this into a compile error rather than a bug.
return switch (d) {
.north, .south => true,
.east, .west => false,
};
}
test "exhaustive switch over an enum" {
try expect(isVertical(.north));
try expect(!isVertical(.east));
}Zig’s switch has no fallthrough, and it must be exhaustive: every possible
value needs a branch, or the program does not compile.
Ranges and multiple values
switch (x) {
-1...1 => ..., // inclusive range
10, 100 => ..., // several values, one branch
else => ...,
}
Note ... for ranges is inclusive on both ends, unlike the .. used in for
ranges, which excludes the upper bound.
The exhaustiveness payoff
When switching over an enum and covering every tag, leave out else:
return switch (direction) {
.north, .south => true,
.east, .west => false,
};
Now adding a .up variant later turns this into a compile error pointing
straight at the code that needs updating. Adding else => false would instead
silently classify the new direction as horizontal. The else branch is a
convenience that costs you the compiler’s help. Spend it deliberately.