Ziglings 笔记 22: 薛定谔的变量 (Error Unions)
要么成功,要么失败
在上一篇笔记中,我们定义了单纯的错误集。但在实际开发中,我们遇到的变量通常具有双重性:正常情况下它是数据,出问题时它是错误。
Ziglings 的第 22 个练习向我们展示了如何用 Error Union (错误联合) 来表达这种概念。
挑战:双重身份
代码声明了一个变量 my_number。
- 一开始,它被赋值为整数
5。 - 随后,它又被赋值为错误
MyNumberError.TooSmall。
我们需要定义一个能够同时兼容这两种情况的数据类型。
解决方案
这是正确的类型定义:
const std = @import("std");
const MyNumberError = error{TooSmall};
pub fn main() void {
// 关键语法:MyNumberError!u8
// 读作:"MyNumberError OR u8"
var my_number: MyNumberError!u8 = 5;
// 现在它存储的是数字 5
// ...
// 现在它存储的是错误 TooSmall
// 由于类型是用 '!' 连接的联合体,这种赋值是合法的
my_number = MyNumberError.TooSmall;
std.debug.print("I compiled!\n", .{});
}
核心知识点总结
1. 惊叹号 ! 的魔力
在 Zig 类型系统中,! 是一个二元操作符,用于创建 Error Union Type。
- 格式:
[ErrorSet]![Type] - 例子:
FileError!u32表示这个值要么是FileError中的一种错误,要么是一个u32整数。
2. 内存效率
你可能会担心这种“联合类型”很占内存。
实际上,Zig 编译器对此做了极度的优化。在很多情况下,它不需要额外的内存来存储“这是错误还是值”的标签,而是利用了指针对齐或保留值的空隙。这使得 Zig 的错误处理比类似 Rust 的 Result<T, E> 往往更紧凑。
3. 这里的用法
虽然在这个练习中我们把变量定义为 MyNumberError!u8,但在实际的函数返回值中,我们经常简写为 !u8。这会让 Zig 自动推导所有可能的错误集,这被称为 Inferred Error Sets。
后续预告:现在我们知道如何创建这种包含错误的变量了,但我们该如何使用它呢?如果它是个错误,程序会崩溃吗?下一篇我们将学习如何使用 catch 关键字来“拆包”这些盒子。