Error Handling
Did you see the !void
return type of the main
function from earlier? That is Zig's way of indicating that the main
function could possibly return an error. Error handling is a feature that was designed carefully in Zig, and worth exploring before we go further.
Defining an error
An error is defined in a similar way to an enum. You can also use ||
to combine different error types together. Here are some errors from the standard library.
// in std/mem/Allocator.zig
pub const Error = error{
OutOfMemory,
};
// in std/io.zig
pub const NoEofError = ReadError || error{
EndOfStream,
};
// in std/dynamic_library.zig
const ElfDynLibError = error{
FileTooBig,
NotElfFile,
NotDynamicLibrary,
MissingDynamicLinkingInformation,
ElfStringSectionNotFound,
ElfSymSectionNotFound,
ElfHashTableNotFound,
} || posix.OpenError || posix.MMapError;
Returning errors
After defining your error, you have to indicate that a function you've written can possibly return an error. This is done by placing the error name, then a !
, then the actual return type.
In the following example, the function myFunction
can only return MyError
. There is no other possible error type. On the other hand, myOtherFunction
could possibly return other error types — its error type is inferred from the function body.
const MyError = error{
FooReason,
BarReason,
AnotherReason,
};
pub fn myFunction(x: i32) MyError!i32 {
if (x < 43) {
// Simply return the error if you encounter an error condition, instead
// of returning the result.
return MyError.FooReason;
} else if (x > 43) {
return MyError.BarReason;
} else {
return 33;
}
}
// Here the error type is inferred, instead of being explicitly defined.
pub fn myOtherFunction(x: i32) !i32 {
if (x < 99) {
return MyError.AnotherReason;
} else if (x > 99) {
return MyError.BarReason;
} else {
return 22;
}
}
Handling errors
You can either use try
to propagate errors from functions that you call, or catch
to handle the errors at the call site. In Zig, errors must be handled (eventually), otherwise you'll get a compiler error.
pub fn main() !void {
// This will propagate the error up to the caller of this function.
// In the case of the `main` function, it would end the program.
const my_function_result = try myFunction(43);
std.debug.print("The result of myFunction is: {}\n", .{my_function_result});
// Instead of propagating the error, you can also handle it.
const my_other_function_result = myOtherFunction(43) catch |err| {
switch (err) {
MyError.FooReason => std.debug.print("FooReason\n", .{}),
MyError.BarReason => std.debug.print("BarReason\n", .{}),
MyError.AnotherReason => std.debug.print("AnotherReason\n", .{}),
}
// Return early from the main function.
return;
};
std.debug.print("The result of myOtherFunction is: {}\n", .{my_other_function_result});
}
Last updated