错误联合类型 #
一、错误联合类型基础 #
1.1 什么是错误联合类型 #
错误联合类型表示一个值可能是成功的结果,也可能是错误:
zig
const std = @import("std");
pub fn main() void {
// 错误联合类型
const result: anyerror!i32 = 42;
const error_result: anyerror!i32 = error.SomethingWentWrong;
std.debug.print("result: {!}\n", .{result});
std.debug.print("error_result: {!}\n", .{error_result});
}
1.2 错误集 #
定义错误类型:
zig
const std = @import("std");
const FileError = error{
NotFound,
PermissionDenied,
TooLarge,
InvalidFormat,
};
fn readFile(path: []const u8) FileError![]const u8 {
if (path.len == 0) {
return error.NotFound;
}
return "file content";
}
pub fn main() void {
const result = readFile("test.txt") catch |err| {
std.debug.print("Error: {}\n", .{err});
return;
};
std.debug.print("Content: {s}\n", .{result});
}
1.3 anyerror #
anyerror 是所有错误的超集:
zig
const std = @import("std");
fn mightFail() anyerror!void {
return error.OutOfMemory;
}
pub fn main() void {
mightFail() catch |err| {
std.debug.print("Caught error: {}\n", .{err});
};
}
二、错误处理操作符 #
2.1 catch 操作符 #
catch 用于捕获错误并提供默认值:
zig
const std = @import("std");
fn divide(a: i32, b: i32) !i32 {
if (b == 0) return error.DivisionByZero;
return @divTrunc(a, b);
}
pub fn main() void {
// 提供默认值
const result1 = divide(10, 2) catch 0;
const result2 = divide(10, 0) catch 0;
std.debug.print("10 / 2 = {}\n", .{result1});
std.debug.print("10 / 0 = {}\n", .{result2});
// 处理错误
const result3 = divide(10, 0) catch |err| blk: {
std.debug.print("Error occurred: {}\n", .{err});
break :blk -1;
};
std.debug.print("result3 = {}\n", .{result3});
}
2.2 try 操作符 #
try 用于传播错误:
zig
const std = @import("std");
fn innerFunction() !i32 {
return error.InnerError;
}
fn outerFunction() !i32 {
// 如果 innerFunction 失败,outerFunction 也会返回错误
const value = try innerFunction();
return value * 2;
}
pub fn main() void {
const result = outerFunction() catch |err| {
std.debug.print("Error: {}\n", .{err});
return;
};
std.debug.print("Result: {}\n", .{result});
}
2.3 try 与 catch 的区别 #
zig
const std = @import("std");
fn mightFail() !i32 {
return error.Failure;
}
pub fn main() !void {
// try: 传播错误给调用者
const value1 = try mightFail();
// catch: 处理错误
const value2 = mightFail() catch |err| {
std.debug.print("Error: {}\n", .{err});
return;
};
_ = value1;
_ = value2;
}
三、errdefer #
3.1 基本用法 #
errdefer 在函数返回错误时执行:
zig
const std = @import("std");
fn allocateAndProcess(allocator: std.mem.Allocator) !void {
const buffer = try allocator.alloc(u8, 100);
errdefer allocator.free(buffer);
// 如果后续操作失败,buffer 会被释放
if (someCondition()) {
return error.ProcessingFailed;
}
// 成功时,调用者负责释放
}
fn someCondition() bool {
return true;
}
pub fn main() void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
allocateAndProcess(allocator) catch |err| {
std.debug.print("Error: {}\n", .{err});
};
}
3.2 errdefer 与 defer 的区别 #
zig
const std = @import("std");
fn example(success: bool) !void {
std.debug.print("Start\n", .{});
defer std.debug.print("defer: Always executed\n", .{});
errdefer std.debug.print("errdefer: Only on error\n", .{});
if (!success) {
return error.Failure;
}
std.debug.print("Success path\n", .{});
}
pub fn main() void {
std.debug.print("--- Success case ---\n", .{});
example(true) catch {};
std.debug.print("\n--- Error case ---\n", .{});
example(false) catch {};
}
四、错误集操作 #
4.1 错误集合并 #
zig
const std = @import("std");
const ErrorA = error{
NotFound,
InvalidInput,
};
const ErrorB = error{
PermissionDenied,
Timeout,
};
const CombinedError = ErrorA || ErrorB;
fn mightFailA() ErrorA!void {}
fn mightFailB() ErrorB!void {}
fn combined() CombinedError!void {
try mightFailA();
try mightFailB();
}
4.2 错误集推断 #
zig
const std = @import("std");
// 编译器自动推断错误集
fn inferredErrors() !void {
if (false) return error.A;
if (false) return error.B;
if (false) return error.C;
}
pub fn main() void {
inferredErrors() catch |err| {
std.debug.print("Error: {}\n", .{err});
};
}
4.3 错误集包含检查 #
zig
const std = @import("std");
const MyError = error{
NotFound,
InvalidInput,
};
pub fn main() void {
const err: MyError = error.NotFound;
if (err == error.NotFound) {
std.debug.print("Item not found\n", .{});
}
// 使用 switch
switch (err) {
error.NotFound => std.debug.print("Not found\n", .{}),
error.InvalidInput => std.debug.print("Invalid input\n", .{}),
}
}
五、错误与可选类型的转换 #
5.1 错误转可选 #
zig
const std = @import("std");
fn mightFail() !i32 {
return error.Failure;
}
pub fn main() void {
// 使用 catch null
const optional: ?i32 = mightFail() catch null;
if (optional) |value| {
std.debug.print("Value: {}\n", .{value});
} else {
std.debug.print("No value\n", .{});
}
}
5.2 可选转错误 #
zig
const std = @import("std");
fn mightBeNull() ?i32 {
return null;
}
pub fn main() !void {
const value = mightBeNull() orelse return error.NoValue;
std.debug.print("Value: {}\n", .{value});
}
六、实际应用 #
6.1 文件操作 #
zig
const std = @import("std");
fn readFileContent(path: []const u8, allocator: std.mem.Allocator) ![]u8 {
const file = try std.fs.cwd().openFile(path, .{});
defer file.close();
const stat = try file.stat();
const content = try allocator.alloc(u8, stat.size);
errdefer allocator.free(content);
const bytes_read = try file.readAll(content);
if (bytes_read != stat.size) {
return error.IncompleteRead;
}
return content;
}
pub fn main() void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const content = readFileContent("test.txt", allocator) catch |err| {
std.debug.print("Error reading file: {}\n", .{err});
return;
};
defer allocator.free(content);
std.debug.print("Content: {s}\n", .{content});
}
6.2 网络操作 #
zig
const std = @import("std");
fn connectToServer(address: []const u8, port: u16) !std.net.Stream {
const addr = try std.net.Address.parseIp(address, port);
return std.net.tcpConnectToAddress(addr);
}
pub fn main() void {
const conn = connectToServer("127.0.0.1", 8080) catch |err| {
std.debug.print("Connection failed: {}\n", .{err});
return;
};
defer conn.close();
std.debug.print("Connected!\n", .{});
}
6.3 解析操作 #
zig
const std = @import("std");
const ParseError = error{
InvalidFormat,
OutOfRange,
EmptyInput,
};
fn parsePositiveInt(input: []const u8) ParseError!u32 {
if (input.len == 0) return error.EmptyInput;
var result: u32 = 0;
for (input) |c| {
if (c < '0' or c > '9') return error.InvalidFormat;
const digit = c - '0';
if (result > (@as(u32, @intCast(std.math.maxInt(u32)) - digit) / 10)) {
return error.OutOfRange;
}
result = result * 10 + digit;
}
return result;
}
pub fn main() void {
const inputs = [_][]const u8{ "123", "abc", "", "999999999999999999999" };
for (inputs) |input| {
const result = parsePositiveInt(input) catch |err| {
std.debug.print("'{s}': Error - {}\n", .{ input, err });
continue;
};
std.debug.print("'{s}': {}\n", .{ input, result });
}
}
七、最佳实践 #
7.1 错误命名 #
zig
// 好:描述性的错误名称
const FileError = error{
NotFound,
PermissionDenied,
IsDirectory,
NotDirectory,
};
// 避免:模糊的错误名称
const BadError = error{
Failed,
Error,
SomethingWrong,
};
7.2 错误粒度 #
zig
// 好:细粒度错误
fn openFile(path: []const u8) !File {
if (!exists(path)) return error.NotFound;
if (!hasPermission(path)) return error.PermissionDenied;
return open(path);
}
// 避免:粗粒度错误
fn openFileBad(path: []const u8) !File {
if (!canOpen(path)) return error.CannotOpen;
return open(path);
}
7.3 错误处理策略 #
zig
const std = @import("std");
fn processItem(item: Item) !void {
// 策略1:传播错误
const data = try loadData(item);
// 策略2:提供默认值
const config = loadConfig() catch defaultConfig();
// 策略3:记录并继续
if (validate(item)) |_| {
// 验证通过
} else |err| {
std.debug.print("Validation warning: {}\n", .{err});
}
_ = data;
_ = config;
}
const Item = struct {};
fn loadData(item: Item) ![]const u8 { _ = item; return "data"; }
fn loadConfig() ![]const u8 { return error.NoConfig; }
fn defaultConfig() []const u8 { return "default"; }
fn validate(item: Item) !void { _ = item; return; }
八、总结 #
错误联合类型要点:
| 操作 | 说明 |
|---|---|
!T |
错误联合类型 |
error{} |
错误集定义 |
try |
传播错误 |
catch |
捕获错误 |
errdefer |
错误时执行 |
下一步,让我们学习控制流!
最后更新:2026-03-27