Zig 学习笔记:标签化 Switch 与状态机
Ziglings 108: 标签化 Switch (Labeled Switch)
在编程中,状态机 (State Machine) 是一种非常常见的设计模式,用于处理对象在不同状态之间的流转。在 C 或其他语言中,我们通常使用 while(true) 循环包裹一个 switch 语句来实现这一点。
Zig 提供了一种更优雅、更“甜”(Syntactic Sugar)的方式:Labeled Switch。
传统方式 vs 新方式
传统方式 (While + Switch)
这是我们在之前的练习中可能会用到的写法:
var state = State.Start;
while (true) {
switch (state) {
State.Start => { state = State.Next; continue; },
State.End => break,
}
}
Zig 的方式 (Labeled Switch)
Zig 允许给 Switch 加上标签,并直接在 case 中通过 continue 传递下一个状态值:
state_machine: switch (State.Start) {
State.Start => continue :state_machine State.Next,
State.End => break :state_machine,
}
这不仅减少了样板代码,还让数据流向更加清晰。
练习:模拟 Pull Request 状态流转
在 Ziglings 108 题中,我们需要修复一个模拟 Pull Request (PR) 生命周期的程序。目标是让 PR 从 Draft 状态一路绿灯走到 Merged。
代码实现
const std = @import("std");
// 定义 PR 的所有可能状态
const PullRequestState = enum(u8) {
Draft,
InReview,
Approved,
Rejected,
Merged,
};
pub fn main() void {
// 这是一个带标签 'pr' 的 switch 语句
// 初始状态是 .Draft
pr: switch (PullRequestState.Draft) {
// 状态 1: 草稿 -> 提交审核
// 使用 continue :pr 更新 switch 的值为 InReview,并重新求值
PullRequestState.Draft => continue :pr PullRequestState.InReview,
// 状态 2: 审核中 -> 批准
PullRequestState.InReview => continue :pr PullRequestState.Approved,
// 状态 3: 批准 -> 合并
PullRequestState.Approved => continue :pr PullRequestState.Merged,
// 错误分支:如果进入这里,函数会直接返回
PullRequestState.Rejected => {
std.debug.print("The pull request has been rejected.\n", .{});
return;
},
// 最终状态:合并
// break :pr 表示跳出名为 pr 的这个 switch 块,继续执行后面的代码
PullRequestState.Merged => break :pr,
}
// 只有执行了 break :pr 才会运行到这里
std.debug.print("The pull request has been merged.\n", .{});
}
核心概念解析
pr: switch (...): 这里的pr是我们给 switch 块起的标签名。continue :pr Value: 这是本题的精髓。它做了两件事:- 跳转回
pr标签的顶部。 - 将 switch 的判断值更新为
Value。
- 跳转回
break :pr: 当状态机达到终点(Merged)时,我们需要跳出这个 switch 结构,继续执行主程序。
通过这种方式,Zig 让我们能够以极低的运行时开销编写清晰、线性的状态逻辑。这也体现了 Zig 的哲学:提供强大的控制流工具,但不隐藏底层的逻辑。