Skip to content

Support error sets in switch cases #2473

Open
@hryx

Description

@hryx

Sometimes it would be useful to handle errors by switching on groups (sets) at a time. I imagine this might especially come in handy when dealing with third party libraries whose functions categorize errors into sets. This feature would keep code a tiny bit more robust against library upgrades: If that third party library extended one of the error sets it returns, your code would still be handling errors of the same "category" with your existing switch prong.

Let's make believe for a moment:

main.zig

const warn = @import("std").debug.warn;
const http = @import("http.zig");

pub fn main() void {
    const res = http.doRequest() catch |err| switch (err) {
        http.ClientError => |e| warn("we goofed: {}\n", e), // error set + payload
        http.ServerError => |_| warn("they goofed\n"), // error set, discard payload
        error.OutOfMemory => warn("{\n}", err), // error
        else => unreachable, // else
    };
    warn("response: {}\n", res);
}

http.zig

pub fn doRequest() (ClientError || ServerError || error{OutOfMemory})![]const u8 {
    return ServerError.NotImplemented;
}

pub const ClientError = error{
    BadRequest,
    Unauthorized,
    Forbidden,
    NotFound,
};

pub const ServerError = error{
    InternalError,
    NotImplemented,
    ServiceUnavailable,
};

Present-day (0.4.0+dbb5da14), running zig build-exe main.zig gives:

error: expected type 'error{BadRequest,Unauthorized,Forbidden,NotFound,InternalError,NotImplemented,ServiceUnavailable,OutOfMemory,}', found 'type'
        http.ClientError => |e| warn("we goofed: {}\n", e), // error set + payload
            ^

Notes

  1. Slightly related: else value when switching on error set should have optional capture value which is subset #769
  2. This proposal originated from a desire in this real-world block:

    zig/std/zig/parse.zig

    Lines 55 to 65 in 063d05a

    node.decls = parseContainerMembers(arena, it, tree, .Keyword_struct) catch |err| {
    // TODO: Switch on the error type
    // https://github.com/ziglang/zig/issues/2203
    // return switch (err) {
    // Allocator.Error => |e| e,
    // Error.UnexpectedToken => tree,
    // };
    if (err == Error.UnexpectedToken) return node;
    assert(err == Allocator.Error.OutOfMemory);
    return Allocator.Error.OutOfMemory;
    };
  3. The payload in this proposal might be completely superfluous given the current design of errors. The above code could use the outer capture err just as well as the prong capture e, since there's nothing to unwrap. (This would not be the case if error sets were unique container values unto themselves rather than global sets -- a separate discussion.)
  4. Interesting bonus that we'd get for free, for what it's worth: anonymous error sets as a prong. Not like that's more useful than a comma-separated multi-case prong -- just a nice example of how Zig's elegant syntax design allows for all sorts of expressions.
switch (err) {
    error{Evil, OutOfMemory} => |e| warn("very bad: {}\n", e),
    else => {},
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    proposalThis issue suggests modifications. If it also has the "accepted" label then it is planned.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions