Noreturn vs error{Usage}!noreturn

I don’t understand how I should be specifying the return type of this function:

fn usageErrorExit(
    options: ParseOptions,
    comptime format: []const u8,
    args: anytype,
) error{Usage} {
    if (options.render_usage_errors) {
        std.log.err(format, args);
        std.log.err("Provide only --help for help.", .{});
    }
    if (options.exit_usage_error) std.process.exit(1);
    return error.Usage;
}

Should I be using error{Usage}!noreturn or just error{Usage} and why?

When I do use the noreturn I have issues getting my callsite to work:

pub fn parse(
    comptime command: Command,
    /// See std.process.Args.toSlice
    /// Index 0 is skipped (typically the program name). No args is a usage error.
    args: []const [:0]const u8,
    options: ParseOptions,
) ParseError!Parsed(command) {
    comptime if (parseRequiresAlloc(command)) @compileError("Parsing requires allocation. See parseAlloc.");
    var iter: Iterator = .init(args);
    const argv0 = iter.next() orelse return usageErrorExit(options, "missing first argument (typically program name)", .{});
    const parsed = try parseRecursive(command, null, &iter, options);
    helpExit(command, argv0, parsed, options);
    return parsed;
}

src/argparser/root.zig:241:59: note: error union payload 'noreturn' cannot cast into error union payload ...
           ^~~~~~
src/argparser/root.zig:238:18: note: function return type declared here
) ParseError!Parsed(command) {

I think if you wanted to use error{Usage}!noreturn you would need to call it with try like

... orelse try usageError(...)

Error union payloads don’t currently coerce to one another. I believe there is a proposal to change this.

4 Likes