Zig Anyerror Patterns
Anyerror is an error set that can hold any error value. It is the widest error set, useful where many different sources meet.
Storing any error
One anyerror variable can hold errors from two different sets.
const std = @import("std");
const ReadError = error{ NotFound };
const NetError = error{ Timeout };
fn store() anyerror {
return ReadError.NotFound;
}
pub fn main() !void {
const out = std.io.getStdOut().writer();
const e: anyerror = store();
try out.print("stored: {s}\n", .{@errorName(e)});
const f: anyerror = NetError.Timeout;
try out.print("also: {s}\n", .{@errorName(f)});
}
Returning from many sets
One function can return errors from different sets when its return type is anyerror.
const std = @import("std");
const ReadError = error{ NotFound };
const NetError = error{ Timeout };
fn pick(which: u8) anyerror!i32 {
if (which == 0) return ReadError.NotFound;
if (which == 1) return NetError.Timeout;
return 5;
}
pub fn main() !void {
const out = std.io.getStdOut().writer();
if (pick(0)) |v| {
try out.print("value: {d}\n", .{v});
} else |err| {
try out.print("error: {s}\n", .{@errorName(err)});
}
if (pick(1)) |v| {
try out.print("value: {d}\n", .{v});
} else |err| {
try out.print("error: {s}\n", .{@errorName(err)});
}
if (pick(2)) |v| {
try out.print("value: {d}\n", .{v});
} else |err| {
try out.print("error: {s}\n", .{@errorName(err)});
}
}
Carrying named errors in
Try carries named errors into the wider return type without extra code.
const std = @import("std");
const ReadError = error{ NotFound };
const NetError = error{ Timeout };
fn read(ok: bool) ReadError!i32 {
if (ok) return 10;
return ReadError.NotFound;
}
fn fetch(ok: bool) NetError!i32 {
if (ok) return 20;
return NetError.Timeout;
}
fn combined(which: u8) anyerror!i32 {
if (which == 0) return try read(false);
if (which == 1) return try fetch(false);
return try read(true);
}
pub fn main() !void {
const out = std.io.getStdOut().writer();
if (combined(0)) |v| {
try out.print("value: {d}\n", .{v});
} else |err| {
try out.print("error: {s}\n", .{@errorName(err)});
}
if (combined(1)) |v| {
try out.print("value: {d}\n", .{v});
} else |err| {
try out.print("error: {s}\n", .{@errorName(err)});
}
if (combined(2)) |v| {
try out.print("value: {d}\n", .{v});
} else |err| {
try out.print("error: {s}\n", .{@errorName(err)});
}
}
Example
const std = @import("std");
const FileError = error{ NotFound, Empty };
const NetError = error{ Timeout };
fn loadFile(ok: bool) FileError!i32 {
if (ok) return 12;
return FileError.NotFound;
}
fn loadNet(ok: bool) NetError!i32 {
if (ok) return 30;
return NetError.Timeout;
}
fn loadAny(source: u8) anyerror!i32 {
if (source == 0) return try loadFile(false);
if (source == 1) return try loadNet(true);
return try loadFile(true);
}
pub fn main() !void {
const out = std.io.getStdOut().writer();
if (loadAny(0)) |v| {
try out.print("value: {d}\n", .{v});
} else |err| {
try out.print("error: {s}\n", .{@errorName(err)});
}
if (loadAny(1)) |v| {
try out.print("value: {d}\n", .{v});
} else |err| {
try out.print("error: {s}\n", .{@errorName(err)});
}
if (loadAny(2)) |v| {
try out.print("value: {d}\n", .{v});
} else |err| {
try out.print("error: {s}\n", .{@errorName(err)});
}
}
Key points
- Anyerror: anyerror is an error set that can hold any error value.
- Any named error can be stored in an anyerror variable, and @errorName still gives its exact name.
- A function returning anyerror can combine helpers from different error sets with try.
- Catching an anyerror works the same way as catching a named set, including comparing one exact error with ==.
- Anyerror hides the list of possible errors from the type, so a caller cannot tell from the signature which errors to expect.
- Prefer a named set when the possible errors are known, and save anyerror for a boundary that joins many sources.
loadAny joins a file source and a network source behind one return type. Each branch keeps its own exact error name, which the caller still reads with @errorName. The trade is clear: the caller gains one simple type and loses the exact list of possible errors.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
Which return type lets one function return both NotFound and Timeout from different sets?