Ziglings 笔记 25: 优雅的 Try
优雅的“甩锅”
在上一篇笔记中,我们为了把错误传递给上层函数,写了类似 catch |err| return err 这样略显繁琐的代码。
Ziglings 的第 25 个练习告诉我们:这种模式太常见了,以至于 Zig 专门为它设计了一个关键字——try。
挑战:简化错误传播
我们有一个 addFive 函数,它调用 detect 函数。
detect可能会报错。- 如果
detect报错,addFive也应该报错(把错误传出去)。 - 如果
detect成功,addFive应该拿到数值并加 5。
解决方案
使用 try 关键字,一行代码搞定:
const std = @import("std");
const MyNumberError = error{
TooSmall,
TooBig,
};
pub fn main() void {
// main 函数负责兜底,使用 catch 0 将错误处理为默认值
const a: u32 = addFive(44) catch 0;
const b: u32 = addFive(14) catch 0;
const c: u32 = addFive(4) catch 0;
std.debug.print("a={}, b={}, c={}\n", .{ a, b, c });
}
fn addFive(n: u32) MyNumberError!u32 {
// 核心代码:try detect(n)
// 意思是:尝试执行 detect(n)。
// 1. 如果成功,把结果赋值给 x。
// 2. 如果失败,立即 return 那个错误(不执行下一行)。
const x = try detect(n);
return x + 5;
}
fn detect(n: u32) MyNumberError!u32 {
if (n < 10) return MyNumberError.TooSmall;
if (n > 20) return MyNumberError.TooBig;
return n;
}
核心知识点总结
1. try = catch |err| return err
这是 Zig 中最常用的语法糖之一。它极大地减少了代码量,让我们可以专注于“成功路径”的逻辑,而不需要像 Go 语言那样在每一行后面写 if err != nil { return err }。
2. 提前返回 (Early Return)
当 try 遇到错误时,它会触发即时返回。这意味着 try 后面的代码(return x + 5)根本不会被执行。
3. 类型兼容性
要在一个函数内部使用 try,这个函数本身的返回类型必须能够容纳错误。
比如 addFive 返回的是 MyNumberError!u32,所以它完全有资格把 detect 产生的 MyNumberError 抛出去。
后续预告:到现在为止,我们的程序执行顺序都是线性的。但如果我想让某些代码(比如清理内存)等到函数结束前那一刻才执行,该怎么办?下一篇我们将学习 Zig 的延时执行机制 —— defer。