Should there be a shortcut for `catch |err| @panic(@errorName(err))`?

Often I don’t want to bother with proper error handling and all the required cleanup, and instead just want to exit the program cleanly.

A lot of people seem to be using catch unreachable for that, but this is wrong for errors that might actually happen (like memory allocation errors) because it just leads to illegal behavior in release builds.

The proper way to exit on an error seems to be something like catch |err| @panic(@errorName(err)), but this is awfully long to type. I think there is some syntax sugar missing, maybe a variation of try that panics instead of returning.

I always have a fn fatal(comptime format: []const u8, args: antype) noreturn for those kinds of things. That way, I can also add some context to the error.

1 Like

I don’t think it’s a good habit to always fall back on catch unreachable or catch @panic(...) instead of handling errors properly or letting them flow up the call stack with try. But if you insist it is pretty easy to implement what you want in user code:

inline fn doOrDie(x: anytype) @typeInfo(@TypeOf(x)).error_union.payload {
    return x catch |err| @panic(@errorName(err));
}

pub fn main() void {
    doOrDie(fail());
}

fn fail() !void {
    return error.Oops;
}
1 Like

I’m thinking about programs like games where it doesn’t make much sense anyway to continue if a memory allocation or other initialization fails. Letting errors bubble up and freeing all resources along the way is easier than in C thanks to defer/errdefer, but you still end up with a lot of duplicate cleanup code.

A problem with a wrapper function like this doOrDie is that apparently ZLS can’t figure out the return value and you loose the type information in the IDE for the result you are actually interested in.

Yes I have the same in a few cases.
If memory allocation fails or std.time.Timer.Start() fails my program is utterly useless. Wondering what is the best way to let everything crash :slight_smile:

I have to study on that ‘noreturn’ thing.

ZLS has a way of improving on this kind of thing over time BTW.

I tried changing it to this and this form provides correct completions.

inline fn doOrDie(x: anytype) @TypeOf(x catch unreachable) {
    return x catch |err| @panic(@errorName(err));
}
4 Likes

Noreturn as a return type means that you can use it wherever a value (or no value) would be expected. Just like unreachable actually. So you can use it on the right side of an assignment like const x = get() catch |err| fatal("got {}", .{err});

2 Likes

If this is mainly about allocator errors, then you can use a custom wrapper allocator to handle OutOfMemory by panicing internally, making it save to catch unreachable out of memory errors, here is an example from my game.

I even went one step further and also made my own allocator interface which doesn’t even return the error in the first place, but that doesn’t help for standard library containers unless you are willing to rewrite those to return no error too.

4 Likes

There’s also std.debug.panic() (which seems equivalent) in case you were not aware.

5 Likes

I wasn’t, thanks!