> There’s a catch, though. Unlike Rust, ErrorType is global to your whole program, and is nominally typed.
What does "global to your whole program" mean? I'd expect types to be available to the whole compilation unit. I'm also weirded out by the fact that zig has a distinct error type. Why? Why not represent errors as normal records?
Zig automatically does what most languages call LTO, so "whole program" and "compilation unit" are effectively the same thing (these error indices don't propagate across, e.g., dynamically linked libraries). If you have a bunch of ZIg code calling other Zig code and using error types, they'll all resolve to the same global error type (and calling different code would likely result in a different global error type).
> distinct error type, why?
The langage is very against various kinds of hidden "magic." If you take for granted that (1) error paths should have language support for being easily written correctly, and (2) userspace shouldn't be able to do too many shenanigans with control flow, then a design that makes errors special is a reasonable result.
It also adds some homogeneity to the code you read. I don't have to go read how _your_ `Result` type works just to use it correctly in an async context.
The obvious downside is that your use case might not map well to the language's blessed error type. In that case, you just make a normal record type to carry the information you want.
On the other hand zig errors can't have any associated value (https://github.com/ziglang/zig/issues/2647). I often find this requires me to store those values in some other big sum type somewhere which leads to all the same problems/boilerplate that the special error type should have saved me from.
If I have multiple errors then that in-out parameter has to be a union(enum). And then I'm back to creating dozens of slightly different unions for functions which return slightly different sets of errors. Which is the same problem I have in rust. All of the nice inference that zig does doesn't apply to my in-out parameter either. And the compiler won't check that every path that returns error.Foo always initializes error_info.Foo.
> What does "global to your whole program" mean? I'd expect types to be available to the whole compilation unit.
I think they mean you only have one global/shared ErrorType . You can't write the type of function that may yeet one particular, specific type of error but not any other types of error.
> You can easily capture the error and conditionally handle it
Sure. But the compiler won't help you check that your function only throws the errors that you think it does, or that your try block is handling all the errors that can be thrown inside it.
> ...the compiler won't help you check that your function only throws the errors that you think it does, or that your try block is handling all the errors that can be thrown inside it.
It will do both of those:
const std = @import("std");
fn throws(i: usize) !void {
return switch (i) {
0 => error.zero,
1 => error.one,
else => error.many,
};
}
fn catches(i: usize) !void {
throws(i) catch |err| {
return switch (err) {
error.one => error.uno,
else => |other| other,
};
};
}
pub fn main() void {
catches(std.os.argv.len) catch |err| {
switch (err) {
// Type error if you comment out any of these:
// note: unhandled error value: 'error.zero'
error.zero => std.debug.print("0\n", .{}),
error.uno => std.debug.print("1\n", .{}),
error.many => std.debug.print("2\n", .{}),
// Type error if you uncomment this:
// 'error.one' not a member of destination error set
//error.one => std.debug.print("1\n", .{}),
}
};
}
It wouldn't hurt to just read the docs before making confident claims.
I said I wasn't speaking for Zig specifically, just on general principle that errors are not really values. Many languages reify errors as values to avoid having different semantics for errors, but errors probably should have their own semantics. Zig seems to take a middle ground here, where errors are a special type of value but that still sort of has its own semantics.
I was generally responding to the whole thread and pointing to how Zig sees errors. Enums are a type of value, yes, but they're typically dealt with differently than other data types.
> There’s a catch, though. Unlike Rust, ErrorType is global to your whole program, and is nominally typed.
What does "global to your whole program" mean? I'd expect types to be available to the whole compilation unit. I'm also weirded out by the fact that zig has a distinct error type. Why? Why not represent errors as normal records?