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", .{});
}

核心概念解析

  1. pr: switch (...): 这里的 pr 是我们给 switch 块起的标签名。
  2. continue :pr Value: 这是本题的精髓。它做了两件事:
    • 跳转回 pr 标签的顶部。
    • 将 switch 的判断值更新为 Value
  3. break :pr: 当状态机达到终点(Merged)时,我们需要跳出这个 switch 结构,继续执行主程序。

通过这种方式,Zig 让我们能够以极低的运行时开销编写清晰、线性的状态逻辑。这也体现了 Zig 的哲学:提供强大的控制流工具,但不隐藏底层的逻辑。