I am forced to handle FakeError in switch cased even though it will never be generated. Shoudn’t the declaration of RealAndFakeError be a compile error?
pub const FooError = error{
WindowsOnlyError,
LinuxOnlyError,
};
pub fn foo() FooError!void {
switch (builtin.os.tag) {
.windows => {
// Note: This block is only evaluated when the target is Windows
return error.WindowsOnlyError;
},
.linux => {
// Note: This block is only evaluated when the target is Linux
return error.LinuxOnlyError;
},
else => {},
}
}
This allows callsites to handle errors in a cross-platform way even if there are certain errors that are only possible on certain platforms.
For real examples of this, see most of the explicit std.fs error sets:
Perhaps it is LSP responsibilty to warn that an error is never created, because its hard for me to go back into my codebase after a while and prune errors.
I don’t think this is 100% knowable due to conditional compilation. Imagine a complex set of comptime logic, some coming from build options from build.zig, etc. It may be that one specific error is only returned when a certain depedency has a certain version, and only when targeting a specific OS, and only when certain CPU features are enabled, etc.
Ignoring that complication, one way to force a compile error in a trivial case would be to just remove the error in question from the error set and see if the compiler gives you a expected type 'error{RealError}', found type 'error{FakeError}' error. But, again, not getting the error may be a false positive since it may still be returned in specific build configurations.
Years into Zig, some advice: use inferred error sets for rapid development but try to convert them to explicit error sets as soon as you can. Likewise, avoid else => in error switches. Its hugely valuable to discover you introduced new error case from the compiler erroring.
Another reason this can be useful is function pointers. The type of a function pointer can’t have an inferred error set, so the function pointer type needs to be a proper superset of the errors returned by any of those functions.
You can see that in this situation, you’d need to handle all of the errors, because the function won’t know the specifics of the function pointer it’s invoking at the time it’s called.
It would be nice for the LSP to do this, yes. But in the meantime, you can replace the error value with error{ThisErrorDoesNotExist}, and the compiler will tell you what the actual error set is. I usually just write that error{E}.
Why should it be? A function declared to be returning a particular type doesn’t imply that it would return every possible value the type is capable of holding.