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. 系统编程中的“变体”
这种数据结构在其他语言中被称为 Variant 或 Sum Type。 它在错误处理、状态机设计、解析器编写(AST 节点)等领域有着巨大的威力。Zig 让这种高级抽象在系统级语言中变得零成本(Zero-cost abstraction)。
后续预告:我们已经掌握了基本的 Switch。但是 Switch 其实比我们想象的要强大得多。除了匹配单个值,它还能匹配范围(Range)吗?下一篇我们将探索 Switch 的更多花样。