Skip to content

Instantly share code, notes, and snippets.

@kfird214
Last active August 3, 2023 08:24
Show Gist options
  • Save kfird214/6174d232a49737a1c0aa1a8a7a956e16 to your computer and use it in GitHub Desktop.
Save kfird214/6174d232a49737a1c0aa1a8a7a956e16 to your computer and use it in GitHub Desktop.

Error, panic or unreachable - Loris Cro

YouTube: Error, panic or unreachable - Loris Cro

You probably never wrote a program that handles all errors correctly

TLDR

Errors

Courteous Assumption

Error handling is complected

@compileError()

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");
        }
    }
    ...
}

@panic()

  • 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
    }
    ...
}

Zig Errors

  • 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 => {...},
    }
    ...
}

unreachable

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 => {...},
}
    ...

Errors as values

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}); },
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment