Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I think you haven't understood my point or maybe I haven't understood yours, and maybe it is because I have misunderstood Zig's approach.

My understanding is that something like `fn f() -> %T` returns a T or an `error`, and the latter can be literally any error value that exists anywhere in the program. It isn't restricted to the actual possible errors that might occur inside f, and thus has the same failings of Rust that you point out (don't know which subset of errors might be returned), except the unknown set of possible errors is likely to be larger.

(And, you can still get all the niceties of Rust's error handling infrastructure like the ? operator if you manually define an error type that is restricted to the exact set of possible errors: there's not a privileged type for how to represent errors.)



If there's any misunderstanding, it's probably mine. :) I'm reasonably familiar with Rust, and I understand your argument; but I only read about Zig a couple weeks ago, so my knowledge there is based only on a little reading and experimentation.

My reading of the release notes:

https://ziglang.org/download/0.2.0/release-notes.html

...is that error sets can be inferred (at both call-site and in the function declaration); and that inferred error-sets on return types are exact, in the sense that an inferred set will only include the actually-possible errors encountered in the call-tree.

To be fair, I don't know whether a function can declare that it may fail with a given, explicitly-declared error-set, where that error-set includes errors that the function actually cannot produce. (E.g., I say I can throw a network-error code, but I perform no network calls.) If this is permissible (i.e, the compiler doesn't complain), then that undercuts my argument a bit. :) On the other hand (again from the release notes), most functions are encouraged to simply declare their return type as !T, where the ! represents "the inferred error-set for the function," and this seems to support my claim.


Zig's errors are actually different and the variants can be statically known. Error type returns are not usually specified since they can usually be inferred. They can be explicitly specified if needed [1].

For example, the following code

    fn errorOrAdd(a: u8, b: u8) !u8 {
        if (a == 2) return error.FirstArgumentIsTwo;
        if (b == 2) return error.SecondArgumentIsTwo;
        return a + b;
    }

    pub fn main() void {
        if (errorOrAdd(0, 0)) |ok| {

        } else |err| switch (err) {
        // will force a compile-error to see what errors we haven't handled
        }
    }
emits this error at compile-time:

    /tmp/t.zig:13:18: error: error.SecondArgumentIsTwo not handled in switch
    } else |err| switch (err) {
                 ^
    /tmp/t.zig:13:18: error: error.FirstArgumentIsTwo not handled in switch
    } else |err| switch (err) {
                 ^
You can use the global `error` type which encapsulates all other error types if needed, replacing `errorOrAdd` in the previous `main` with the following function

    fn anyError() error!u8 {}
now requires an else case, since it can be any possible error.

    /tmp/t.zig:13:18: error: else prong required when switching on type 'error'
        } else |err| switch (err) {
                     ^
This works pretty well and is very informative in most cases. The tradeoffs are it can make some instantiation of sub-types a bit clunky [2] and you need to avoid the global error type everywhere. The global error infests all calling functions and makes their error returns global as a result. You can however catch this type and create a new specific variant so there is a way around this for the caller at least.

Do remember that Rust allows passing information in the Err variant of a Result while Zig's error codes are just that, codes with no accompanying state.

[1] https://github.com/tiehuis/zig-bn/blob/3d374cffb2536bce80453...

[2] https://github.com/tiehuis/zig-deflate/blob/bb10ee1baacae83d...


> Zig's errors are actually different and the variants can be statically known. Error type returns are not usually specified since they can usually be inferred. They can be explicitly specified if needed [1].

Yep, as a sibling points out, I was working with a long-ago version of Zig's error handling.


ErrorSets have been in the language for some time now. The syntax you are referencing is pretty outdated now too, when considering the pace of language changes here in Zig's pre-1.0 days.

https://ziglang.org/documentation/master/#Error-Set-Type


Ah that explains it: I was looking at the now-old https://andrewkelley.me/post/intro-to-zig.html#error-type




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: