Ziglings 笔记 32: 不可能到达的任务 (Unreachable)

相信我,这不可能发生

在 Ziglings 的第 32 个练习中,我们遇到了一个非常特殊的关键字:unreachable

在编程中,我们经常会遇到这样的情况:逻辑上我们知道某个变量只可能是 A 或 B,但类型系统认为它可能是 A 到 Z。在 Zig 中,我们必须显式地处理这种情况。

挑战:微型虚拟机的指令集

我们编写了一个简单的解释器,它读取指令数组并修改数值。

  • 指令 1: 加 1
  • 指令 2: 减 1
  • 指令 3: 平方

虽然我们的指令数组里只有 1, 2, 3,但 op 变量的类型是 u8(0-255)。Zig 编译器抱怨我们的 switch 没有处理剩下的 253 种情况。

解决方案

我们使用 unreachable 来填补这个缺口:

const std = @import("std");

pub fn main() void {
    const operations = [_]u8{ 1, 1, 1, 3, 2, 2 };
    var current_value: u32 = 0;

    for (operations) |op| {
        switch (op) {
            1 => {
                current_value += 1;
            },
            2 => {
                current_value -= 1;
            },
            3 => {
                current_value *= current_value;
            },
            // 核心修复:
            // 我们向编译器保证:op 绝对不可能是 1, 2, 3 以外的值。
            else => unreachable,
        }

        std.debug.print("{} ", .{current_value});
    }

    std.debug.print("\n", .{});
}

核心知识点总结

1. 什么是 unreachable

它不仅仅是“什么都不做”。它是一份契约。 当我们写下 unreachable 时,我们是在对编译器说:“如果程序运行到这一行,那就是由于逻辑错误导致的 Bug。”

2. 安全与性能的权衡

unreachable 的行为取决于构建模式:

  • Debug 模式:如果代码真的跑到了这里,程序会安全地 Panic(崩溃),并在终端打印错误信息。这能帮我们发现 Bug。
  • ReleaseFast 模式:编译器会利用“这不可能发生”的假设进行优化。它可能会完全删除边界检查代码。如果此时真的跑到了这里,就会触发未定义行为 (Undefined Behavior)

3. 何时使用?

  • 穷尽 Switch:当你知道枚举或整数只能是特定几个值时。
  • 逻辑断言:比如在数学计算后,确定结果不可能为空。
  • 迭代器:当你确定还有下一个元素,但 API 返回 Optional 时。

后续预告:我们一直在用 if 处理错误,用 switch 处理状态。但如果我想同时检查返回值是否为错误,如果是错误就处理,不是错误就返回值,有没有更优雅的写法?下一篇我们将学习 iferror union 的结合:if (foo) |value| else |err|