The previous try() proposal added new control flow. That's part of the reason it was criticized so heavily -- it hid a control flow construct in something that looked like a function.
The previous try() proposal added new control flow. That's part of the reason it was criticized so heavily -- it hid a control flow construct in something that looked like a function.
It's both. Adding only concise flow control, such as with the ? operator, works poorly if all you can return is multiple non-generic values; various common permutations get awkward in that case. And as you point out Result<> doesn't achieve much without the concise operators. Rust has very successfully shown the benefit of the combination.
I like there's a language that has such a strong vision, but dealing in absolutes has drawbacks, a little sytactic sugar would make error handling a lot nicer.
I do appreciate rusts match syntax though.
Absolutely false. When you write the really groundbreaking and important software "feels right" is absolutely not what you should be experiencing.
The correct mode you should be in is "holy fuck how did I get here, and can I come out in one piece"?
(A: no, you can't, but the new you will be a better you.)
From what I know of zig’s error handling, I really don’t think it’s better than result types.
It is less work, especially when using error unions (though I think that makes it easier to miss changes to error sets as they’re implicit), but since errors are literally just u16 they’re also deeply lacking in richness and composability.
It’s definitely an improvement over C’s error style, and thus perfectly understandable given zig’s goal, but I very much disagree that it’s “better than Result”.
It’s also extremely magical in that it needs special language-level support throughout in ways Result is not, but obviously that’s more of a matter of taste.
That's a peculiar observation, it's like saying Rust's borrow checker is magical because it needs language-level support. I mean, that's the point.
In any case re: zig error or rust's Result, I don't think either of us are discussing features and pros/cons, but just describing what we like the best. Not very objective an argument.
> but since errors are literally just u16 they’re also deeply lacking in richness and composability
Zig errors are composable: https://ziglang.org/documentation/master/#Merging-Error-Sets and later sections.
You can’t do borrow checking without language support (I think, at least not without a significantly more expressive type system) whereas Result is pretty much just a type, the features it uses are mostly non-exclusive, and those which are, are very much intended not to remain so (e.g. the `Try` trait for the `?` operator).
> Zig errors are composable: https://ziglang.org/documentation/master/#Merging-Error-Sets and later sections.
Not really? That’s just merging error sets, but you can’t say that you’ve got an error which originally comes from an other error, except by creating its dual and documenting it as such. Essentially your choices are full transparency or full type erasure.
What you just described are error traces. They're generated by Zig auomatically for you, thanks also to the fact that errors are a special construct in the language.
Relevant langref passage:
https://ziglang.org/documentation/master/#Error-Return-Trace...
Sum types are a powerful language feature, but they’re also a general language feature, they don’t exist for the sole purpose of creating a Result type (and indeed a number of languages have had the former and lacked the latter).
And affine types are not here for borrow checking, it’s closer to the opposite. Affine types are a formalisation of RAII, borrow checking is a way to make references memory-safe without runtime overhead which mostly avoids unnecessary (and sometimes impossible requiring allocations & refcounting) copies of affine types.
I mean there's nothing stopping you in zig from creating a rich "error" struct and returning a union of the struct with whatever you would have done otherwise. You only lose the error return trace.
Thus Rust's std::result::Result<T, E> where the E in Err(E) is anything that implements the std::result::Error trait.
Rust got this right. It satisfies most use cases with the least possible noise and accommodates the weird ones easily, and does it in a std:: codified manner where no one has to wonder whether or not there is anything stopping them.
If there is a problem with how it's done in zig, it's a community one: maybe the pattern needs a megaphone beside it, like examples in the main docs or tuts in the various zig tut sites that have cropped up
No, and I'm not arguing that zig hasn't done well. I do argue that Go has not; you're only non-weird choice is to fetter code with miles of "if err ==/!=" gymnastics.
Traces are frequently very useful; precise traces have saved me a lot of pain in life. Lack of traces precludes nothing for me, but I have enjoyed the benefit of them enough times to acknowledge their great value.
People can now create Try or Option monads instead of returning the error.
This was the one of the reasons anti generics people were anti generics
I can't see it myself, but happy to educate myself.
But more verbose? I'm not so sure.
At the very least, I'll appreciate having some more sugar over loops that are essentially just map/filter.
Before Rust 1.12, Rust has a macro, named try! which does a similar thing to its argument although the built-in operator has some fancier features.
Yes, it was the second proposal by the Go team for shorter error handling: https://github.com/golang/proposal/blob/master/design/32437-...
> I'm really curious what happened to the proposals for shorter Go error handling
The community didn't like it and it was declined: https://github.com/golang/go/issues/32437#issuecomment-51203...