Ziglings 笔记 33: If 也能处理错误
分岔路口
在之前的笔记中,我们使用了 try(直接甩锅)和 catch(提供备胎)来处理错误。但在 Ziglings 的第 33 个练习中,我们遇到了一种更细腻的处理方式。
有时候,我们需要在成功时使用那个值,而在失败时根据具体的错误原因做不同的事情。这时,If 错误处理表达式就派上用场了。
挑战:精准报错
代码要求我们遍历一组数字:
- 如果数字正常(等于 4),打印它。
- 如果数字不正常(返回错误),我们需要判断是太大了还是太小了,并打印相应的提示。
解决方案
这是使用 if-else 配合 switch 的实现:
const std = @import("std");
const MyNumberError = error{
TooBig,
TooSmall,
};
pub fn main() void {
const nums = [_]u8{ 2, 3, 4, 5, 6 };
for (nums) |num| {
std.debug.print("{}", .{num});
const n = numberMaybeFail(num);
// 核心语法:if (result) |value| ... else |err|
// 这是一个专门用于处理 Error Union 的 if 变体
if (n) |value| {
// 成功路径:value 是解包后的 u8
std.debug.print("={}. ", .{value});
} else |err| switch (err) {
// 失败路径:err 是捕获到的错误
// 我们使用 switch 来区分具体的错误类型
MyNumberError.TooBig => std.debug.print(">4. ", .{}),
// 补充缺失的 Case:
MyNumberError.TooSmall => std.debug.print("<4. ", .{}),
}
}
std.debug.print("\n", .{});
}
fn numberMaybeFail(n: u8) MyNumberError!u8 {
if (n > 4) return MyNumberError.TooBig;
if (n < 4) return MyNumberError.TooSmall;
return n;
}
核心知识点总结
1. if 的多面性
我们之前学过 if (bool),现在我们看到了 if (error_union)。
Zig 复用了 if 关键字来处理完全不同的场景,这减少了关键字的数量。编译器会根据括号里变量的类型自动判断这是普通的条件判断,还是错误解包。
2. 负载捕获 (Payload Capture)
这里的 |value| 和 |err| 再次展示了 Zig 的捕获语法。
- 成功块中,我们拿到了安全可用的数据。
- 失败块中,我们拿到了错误对象。
这种将“控制流”和“数据解包”结合在一起的设计,避免了像 C 语言那样先检查
err != 0再去读数据的分离感。
3. 何时使用?
- 如果你想失败时直接退出 -> 用
try。 - 如果你想失败时给个默认值 -> 用
catch。 - 如果你想失败时打印日志或根据错误类型做不同补救 -> 用
if (...) else |err|。
后续预告:我们已经彻底掌握了 Zig 的基本控制流和错误处理。接下来,我们要迎来第四次阶段性测验 (Quiz 4),它将综合考察我们的实战能力。