YouTube: Error, panic or unreachable - Loris Cro
You probably never wrote a program that handles all errors correctly
- Zig Errors Nice! No Payloads. very little overhead.
- @panic()
- Runtime, don't be lazy. If you are writing a library avoid using it.
- unreachable
- Performance, checked in safe modes
- @compileError()
- Comptime safety, better APIs!
- Errors as values
- Need Payloads?
Error handling is complected
Error that are discoverd at compile time.
fn upper_case_mem_eq(comptime rhs: []const u8, rhs: []const u8) bool {
inline for (rhs) |c| {
if (c >= 'a' and c <= 'z') {
@compilerError("`rhs` must be upper case");
}
}
...
}
- Unrecoverable error condition discovered at runtime
- Not good to leave inside libraries
- Most of the time equivalent of
"TODO: error handling"
Sometimes it is usefull to just crush and let the program/ micro service orchestrator to spin up the process again (e.g Kubernetes)
When the error is complitely out of your application scope to handle. For example when you cannot create a socket listener for you web server.
fn bad(a: *meme.Allocator) void {
const buf = readLine(a);
if (buf.len == 0) {
@panic("error");
// TODO: error handling
}
...
}
- Recoverable errors conditions discovered at runtime
- Error codes only, no messages. (enums)
- Error union with a function's return value. (i.e they are not exceptions)
- Zig have syntax to handle those eroros (try, catch, if)
fn next(buf: []const u8) !usize {
const n = try std.math..parseInt(usize, buf, 10);
return n + 1;
}
next("banana");
error: InvalidCharacter
...
lib/zig/std/fmt.zig:10004:5: ...
return parseWithSign(T, buf, radix, .Pos);
^
test/hello.zig:14:15: ...
const n = try std.math..parseInt(usize, buf, 10);
^
Recoverable version
fn next(buf: []const u8) !usize {
const n = try std.math..parseInt(usize, buf, 10) catch |err| {
error.Overflow => {...},
error.InvalidChar => {...},
}
...
}
peculiar and sharp tool, promising the complier that the code is logically unreachable.
On Debug or ReleaseSafe builds the unreachable turns into somthing like a @panic On ReleaseFast this is an undefined behavior
const buf: [3]u8 = ...;
const n = try std.math..parseInt(i32, buf, 10) catch |err| {
error.Overflow => unreachable,
error.InvalidChar => {...},
}
...
Zig doesn't support errors with payloads. for errors with more context for example. You should implement them yourself.
const OrError(OkType: type) type {
return union(enum) {
Nil = void,
Ok = OkType,
Err = []const u8
}
}
var res = try requestApiIntegerResponce(OrError(i64), ...);
switch (res) {
.Nil => { },
.Err |err | => { std.debug.print("ERROR: {s}", .{err}); },
.Ok |myInt| => { std.debug.print("Got integer: {}", .{myInt}); },
}