Zig用错误集替代异常和错误码。错误是编译期枚举值,零开销传递:
const std = @import("std");
// 定义错误集
const OpenError = error{
FileNotFound,
PermissionDenied,
DiskFull,
IsDir,
};
const ReadError = error{
EndOfStream,
ConnectionReset,
};
// 错误集可以合并
const IoError = OpenError || ReadError;
pub fn main() !void {
// 错误值
const err: OpenError = error.FileNotFound;
// 错误可以比较
std.debug.print("是FileNotFound: {}\n", .{err == error.FileNotFound});
// 获取错误名称
std.debug.print("错误名: {s}\n", .{@errorName(err)});
// 全局错误集——任意错误
const any_err: anyerror = err;
_ = IoError; _ = any_err;
}
E!T表示"返回T或者错误E":
const std = @import("std");
// 显式错误集:!T 等价于 anyerror!T
fn parseU8(str: []const u8) !u8 {
const val = try std.fmt.parseUnsigned(u8, str, 10);
return val;
}
// 精确错误集
fn safeDivide(a: i32, b: i32) error{DivByZero, Overflow}!i32 {
if (b == 0) return error.DivByZero;
const result = @divTrunc(a, b);
// 检查溢出
if (a != 0 and result * b != a) return error.Overflow;
return result;
}
pub fn main() !void {
// 精确错误集让编译器推断可能的所有错误
const result = safeDivide(10, 3);
const val = result catch |err| switch (err) {
error.DivByZero => 0,
error.Overflow => std.math.maxInt(i32),
};
std.debug.print("safeDivide(10,3) = {}\n", .{val});
// 类型推断
const x: u8 = try parseU8("42");
std.debug.print("parsed: {}\n", .{x});
}
错误联合类型在内存中用额外位标记成功/错误,比C的返回码+errno更高效:返回值和错误状态在一次寄存器操作中传递,无需访问全局errno。Debug模式包含错误跟踪,ReleaseFast自动消除。
const std = @import("std");
// try: 成功返回值,失败向上传播错误
fn readConfig() ![]const u8 {
const file = try std.fs.cwd().openFile("config.txt", .{});
defer file.close();
var buf: [1024]u8 = undefined;
const reader = file.reader();
const len = try reader.readAll(&buf);
return buf[0..len];
}
pub fn main() !void {
// catch处理错误
const config = readConfig() catch |err| switch (err) {
error.FileNotFound => "default",
error.PermissionDenied => "denied",
else => return err, // 未知错误继续传播
};
// catch提供默认值
const val = parseU8("abc") catch 0;
// catch返回错误
const num = parseU8("99") catch unreachable;
// if-try组合
if (readConfig()) |data| {
std.debug.print("配置: {s}\n", .{data});
} else |err| {
std.debug.print("读取失败: {}\n", .{err});
}
_ = config; _ = val; _ = num;
}
fn parseU8(str: []const u8) !u8 {
return std.fmt.parseUnsigned(u8, str, 10);
}
const std = @import("std");
// errdefer + 捕获错误值
fn complexInit(allocator: std.mem.Allocator) !void {
const buf1 = try allocator.alloc(u8, 1024);
errdefer allocator.free(buf1);
const buf2 = try allocator.alloc(u8, 2048);
errdefer allocator.free(buf2);
const buf3 = try allocator.alloc(u8, 4096);
errdefer allocator.free(buf3);
// 如果buf3分配失败,buf1和buf2会被自动释放
// 如果buf2分配失败,buf1会被自动释放
// 成功路径
allocator.free(buf3);
allocator.free(buf2);
allocator.free(buf1);
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
try complexInit(gpa.allocator());
std.debug.print("初始化成功\n", .{});
}
Debug模式下Zig提供完整错误跟踪,比C的backtrace更有用:
// 编译时加 --debug-include-errors 或用 Debug 模式
// 运行时错误会显示完整调用链
fn deep1() !void { try deep2(); }
fn deep2() !void { try deep3(); }
fn deep3() !void { return error.DeepError; }
// 错误跟踪会显示:
// error: DeepError
// deep3 at main.zig:3
// deep2 at main.zig:2
// deep1 at main.zig:1
const std = @import("std");
// 重试模式
fn retryableOp(max_retries: u32) !void {
var attempts: u32 = 0;
while (attempts < max_retries) : (attempts += 1) {
return mightFail() catch |err| switch (err) {
error.Transient => {
std.debug.print("重试 {}/{}\n", .{ attempts + 1, max_retries });
continue;
},
else => return err,
};
}
return error.MaxRetriesExceeded;
}
const TransientError = error{ Transient, Permanent };
fn mightFail() TransientError!void {
if (std.crypto.random.int(u1) == 0) return error.Transient;
return;
}
// 错误转换模式
fn wrapIoError(err: std.fs.File.OpenError) error{ConfigError} {
_ = err;
return error.ConfigError;
}
pub fn main) !void {
try retryableOp(3);
std.debug.print("操作成功\n", .{});
}
编写一个读取配置文件的函数链,每步都可能失败,使用try传播
实现一个HTTP请求重试函数,对临时错误重试3次
用errdefer实现3步资源分配,任意步骤失败都正确释放已分配资源
为JSON解析器定义详细错误集:语法错误、类型错误、溢出等
实现一个函数将底层IO错误映射为业务层错误