Labeled switch
in Zig
Zig's new labeled switch
simplifies state transitions by allowing continue
statements to target specific switch cases. This is especially useful for
building state machines like tokenizers or parsers.
Simplifying state machines
In state machines, transitioning between states often involves loops or
conditionals, which can be difficult to manage. With labeled switch
, you can
write more concise code.
state: switch (State.start) {
.start => switch (self.buffer[self.index]) {
'a'...'z' => {
result.tag = .identifier;
continue :state .identifier;
},
else => continue :state .invalid,
},
.identifier => {},
.invalid => {},
}
Labeled switch
lets you label the switch and use continue
to jump to a
specific case. You label the switch
statement (state:
) and use continue :state
to jump directly to another case. This avoids the need for conditionals
and loops.
The simplified example from the release
notes shows
the difference between traditional loops and labeled switch
:
test "emulate labeled switch" {
var op: u8 = 1;
while (true) {
switch (op) {
1 => { op = 2; continue; },
2 => { op = 3; continue; },
3 => return,
4 => {},
}
break;
}
return error.Unexpected;
}
While with labeled switch
you can write:
test "labeled switch" {
foo: switch (@as(u8, 1)) {
1 => continue :foo 2,
2 => continue :foo 3,
3 => return,
4 => {},
}
return error.Unexpected;
}
Performance
Labeled switch
improves CPU branch prediction in hot loops like instruction
dispatch or finite state machines. Traditional loops force the CPU to handle
indirect branches, which can lead to inefficient branch prediction:
.LBB0_3:
xor edi, edi
call analyze_add
jmp .LBB0_15
The machine first jumps back to .LBB0_15
, rechecks the loop condition, and
then computes which case to execute next.
Labeled switch
enables direct computed jumps between cases, reducing
mispredictions.
.LBB0_3:
mov edi, 2
call analyze_addwrap
mov dword ptr [r14 + rbx], eax
add rbx, 4
mov eax, dword ptr [r15 + rbx]
jmp qword ptr [8*rax + .LJTI0_0] ; Computed jump based on state
In this version, instead of jumping back to a central point, each case computes and directly jumps to the next case in the state machine.
Further reading: