Ziglings 笔记 57: 自动推导的标签 (Inferred Enums)

少写代码,多做实事

在上一篇笔记中,我们为了给联合体加上标签,写了一大堆“样板代码”:先定义枚举,再绑定枚举。如果我修改了联合体的字段,还得记得去修改枚举,这很容易出错。

Ziglings 的第 57 个练习告诉我们:编译器可以帮我们干这个脏活!

挑战:删除显式枚举

Doctor Zoraptera 删除了 InsectStat 枚举。我们需要修改 Insect 的定义,使其不需要显式的枚举类型,依然能保持 Tagged Union 的特性。

解决方案

只需将 union(InsectStat) 改为 union(enum)

const std = @import("std");

// 核心修改:使用 union(enum)
// 编译器看到这个,会在幕后自动生成一个包含 flowers_visited 和 still_alive 的枚举。
const Insect = union(enum) {
    flowers_visited: u16,
    still_alive: bool,
};

pub fn main() void {
    const ant = Insect{ .still_alive = true };
    const bee = Insect{ .flowers_visited = 17 };

    std.debug.print("Insect report! ", .{});

    printInsect(ant);
    printInsect(bee);

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

fn printInsect(insect: Insect) void {
    // 使用方式完全没变!
    // 依然可以使用 switch 和 payload capture
    switch (insect) {
        .still_alive => |a| std.debug.print("Ant alive is: {}. ", .{a}),
        .flowers_visited => |f| std.debug.print("Bee visited {} flowers. ", .{f}),
    }
}

核心知识点总结

1. union(enum) 的便利性

这是 Zig 极简主义哲学的体现。 如果这个标签枚举只会被这个联合体用到,那么给它起个名字(比如 InsectStat)其实是多余的。union(enum) 让我们在定义数据结构时一气呵成。

2. 强类型保证

虽然枚举是自动生成的,但它依然是强类型的。你不能把一个 Vehicle 联合体的 .car 标签传给 Insect 联合体。编译器会严格检查字段名匹配。

3. 系统编程中的“变体”

这种数据结构在其他语言中被称为 VariantSum Type。 它在错误处理、状态机设计、解析器编写(AST 节点)等领域有着巨大的威力。Zig 让这种高级抽象在系统级语言中变得零成本(Zero-cost abstraction)。


后续预告:我们已经掌握了基本的 Switch。但是 Switch 其实比我们想象的要强大得多。除了匹配单个值,它还能匹配范围(Range)吗?下一篇我们将探索 Switch 的更多花样。