Description
Motivation
When working with explicit error sets, occasionally I'd like to be able to narrow an existing error set type. A simple example for demonstration:
pub const FooError = error{
A,
/// Windows-only
B,
};
pub fn foo(v: u8) FooError!void {
// ...
}
// Would like to narrow this to exclude `error.B`
pub const BarError = FooError;
pub fn bar(v: u8) BarError!void {
if (builtin.os.tag == .windows) @compileError("not supported on Windows");
// Calls foo but we now know that error.B is unreachable
return foo(v) catch |err| switch (err) {
// Narrow the returned errors to exclude B
error.B => unreachable,
else => |e| return e,
};
}
In this example, it would be nice to be able to narrow BarError
to exclude error.B
while still being able to define it in terms of FooError
.
For a real world example, see here:
// This is os.ReadLinkError || os.RealPathError with impossible errors excluded
pub const SelfExePathError = error{
FileNotFound,
AccessDenied,
NameTooLong,
NotSupported,
NotDir,
// ... truncated ...
} || os.SysCtlError;
where in order to exclude a few errors like error.InvalidWtf8
, error.InvalidUtf8
, the os.ReadLinkError || os.RealPathError
error set type had to be redefined from scratch.
The proposal
This proposal is for some mechanism to exclude errors from an error set type--essentially the opposite of the ||
operator. Another operator would likely make the most sense, something like ^^
.
In the above examples, this would mean BarError
could be defined like so:
pub const BarError = FooError ^^ error{B};
and SelfExePathError
could be defined like this:
pub const SelfExePathError = os.ReadLinkError || os.RealPathError || os.SysCtlError ^^ error{
InvalidWtf8, // selfExePath only converts from WTF-16 -> WTF-8 which cannot fail
InvalidUtf8, // selfExePath is not supported on WASI
};
In terms of implementation, I'd imagine it would respect normal order of operations, e.g.
error{ A, B } || error{B} ^^ error{B} // -> error{A}
error{ A, B } ^^ error{B} || error{B} // -> error{ A, B }