I thought this would work
test "doesn't work" {
const err = getErr();
try testing.expectError(Error1, err);
}
but i got expected type 'anyerror', found 'type'
Heres an example of working code, but as you can see it doesn’t look pretty, and would get annoying if the Error1
were to grow larger or smaller
const std = @import("std");
const testing = std.testing;
const Error1 = error{ Foo, Bar };
const Error2 = error{ Baz, Qux };
const Error = Error1 || Error2;
fn getErr(condition: bool) Error!void {
if (condition) {
return Error2.Qux;
}
return Error1.Foo;
}
test "does work" {
getErr(false) catch |errors| switch (errors) {
Error1.Bar, Error1.Foo => return,
else => return error.IncorrectErrors,
};
return error.IsNotError;
}
Is there a better way to check if an error received is within a set of errors? or am i going about this the wrong way and should use errors differently, if so please point me to the right direction.
1 Like
n0s4
February 11, 2025, 11:10pm
2
expectError
takes a specific error value as it’s first argument, e.g Error1.Foo
, not a whole error set.
Here’s a function which tests that a result is an error within the given error set, it takes a bit of type reflection:
fn expectErrorSet(ErrorSet: type, result: anytype) !void {
if (result) |_| {
return error.NotAnError;
} else |err| {
if (@typeInfo(ErrorSet).error_set) |error_set| for (error_set) |err_info| {
if (std.mem.eql(u8, @errorName(err), err_info.name)) {
return;
}
};
return error.NotInErrorSet;
}
}
test "does work" {
try expectErrorSet(Error1, getErr(false));
try std.testing.expectError(
error.NotInErrorSet,
expectErrorSet(Error1, getErr(true)),
);
}
4 Likes
Thank you, that’s exactly whats I’m looking for.
EDIT: Nevermind; realized you want something that works on a runtime error.
comptime error set helper function stuff
There are some nice helper functions for this (and other error set stuff) in this PR:
ziglang:master
← rohlem:PR-std-error_sets
opened 12:50PM - 26 Feb 24 UTC
As per recommendation by @squeek502 in https://github.com/ziglang/zig/issues/190… 08#issuecomment-1957838077 ,
this PR adds utiliy helper functions in a new module `std.error_set` to the standard library, which closes #19008 .
The naming of `std.error_set` IMO makes `std.error_set.contains(Set, err)` sound more natural than `error_sets.contains`/`contained`,
though precedent in `std.enums` would have lead to `std.error_sets`.
I've written it the way I privately write code, with explicit asserts for invalid inputs and same-line comments,
so it's clear which incorrect usage scenarios were considered.
From what I remember other places in `std` have kept assertions at a minimum,
so feel free to remove / tell me to remove any you deem unnecessary.
Also note that the current implementations of `Excluding` and `Intersect` are based on comptime-executed `inline for`.
With accepted proposal https://github.com/ziglang/zig/issues/2473 implemented, we could instead more directly communicate them to the compiler:
```zig
pub fn Excluding(comptime BaseErrorSet: type, comptime ToExcludeErrorSet: type) type {
return @TypeOf(switch(@as(BaseErrorSet, undefined)) {
ToExcludeErrorSet => unreachable, //blocked by #2473
else => |e| e;
});
}
pub fn Intersect(comptime ErrorSetA: type, comptime ErrorSetB: type) type {
return @TypeOf(switch(@as(ErrorSetA, undefined)) {
ErrorSetB => |e| e, //blocked by #2473
else => unreachable;
});
}
```
Additionally, the status-quo type system doesn't have a way to express a restricted non-exhaustive error set,
so `Excluding(anyerror, error{A})` can't express that `error.A` is excluded and has to return `anyerror` again.
This alternative implementation would however not be coupled with `std.builtin.Type` structure and be naturally compatible with such upgrades.
Unfortunately it was rejected, but you could copy what you want into your project.