13 ms·
Rust: The Error Handling Project Group
- tmpfs 6y agoRust's error handling really made me fall for the language but there are a couple of pain points for me. As others have pointed out the inability to use the `?` operator when a function does not return `Result`, it looks like solutions are in the works for this. But the one i have not been able to figure out is handling errors neatly at async boundaries, ie, when calling sync code that returns `Result` inside a `TryFuture` - is there a pattern for this?
- EugeneOZ 6y agoMy favorite part of Rust. Please just leave it as is ;)
- berkes 6y agoMy thought too, was 'my favorite part of Rust'. But the second thought was 'I'm excited to see it becoming even better'.
- mojzu 6y agoThey've definitely earned some credit in my eyes for the async/await syntax. There was quite a bit of pushback for deviating from the norm there, but after using it for a few months other languages async/await just feels clunky to me now. Rust error handling is the best I've used but if there's a way to make it better then I'm up for it.
- Qwuke 6y agoAs someone who was on the fence about it, I feel exactly the same. The way the Rust maintainers and proponents of post-fix await helped create broader community consensus was impressive in itself too.
- octoberfranklin 6y agoI think that compromise was brilliant, by the way. The only other C-family language with that syntax (dot followed by keyword) I know of is Java (where you can write foobarbaz.class; "class" is a language keyword) and it always looked weird. The Rust maintainers found a great use for it: getting chaining (foo.await?.bar()?.await...) without adding a new postfix "sigil".
- octoberfranklin 6y ago... only after they ship try_blocks! https://doc.rust-lang.org/nightly/unstable-book/language-features/try-blocks.html https://doc.rust-lang.org/nightly/unstable-book/language-fea... Seriously, otherwise you can't do error handling within the body of a function that doesn't return Result<>. The language shouldn't force refactoring decisions on the programmer like that.
- zozbot234 6y ago> Seriously, otherwise you can't do error handling within the body of a function that doesn't return Result<>. Sure you can, just use an inner closure that does return Result<>, and use `match` or the like for error handling. It's not any more verbose than this `try {} ... catch {}` special syntax.
- octoberfranklin 6y ago> It's not any more verbose Seriously? I think this is way less readable: (|| -> Result<_,_> { bar(foo()?) })() than this: try { bar(foo()?)? } Also try_blocks don't have to have a "catch". It's just five characters: "t" "r" "y" "{" "}". I don't see a need for "catch" either.
- Arnavion 6y agoThe closure version would actually be (|| -> Result<_,_> { Ok(bar(foo()?)?) })() to be equivalent, since bar's Err type might need a From::from conversion via `?`. So the try-blocks version is even better in comparison. That said, if you include the `-> Result<_,_>` annotation in the closure case, you also need to include the `: Result<_,_>` annotation for the binding that the try-expr is being assigned to, otherwise the compiler won't know which impl of `std::ops::Try` to use. Of course, you can also use an annotation on the binding in the closure case and get rid of the annotation entirely. That is, the comparison is of: let r: Result<_, _> = (|| Ok(bar(foo()?)?))(); vs let r: Result<_, _> = try { bar(foo()?)? };
- 6y ago
- rklaehn 6y agoWith the anyhow crate and ? syntax I find current rust error handling a pleasure to work with. Of course there are a few rough edges, but nothing major. I also don't mind having to wrap the successful result of a fn in Ok(...). I hope "do a few minor things, add better documentation, but leave the language itself as it is" is also one of the possible outcomes...
- tadzik_ 6y agoIndeed. Perhaps standarizing something along the lines of anyhow/thiserror and documenting it as an upgrade path for those who want it is all they need to do. Or maybe there's some cool ideas yet undiscovered. Great to see that it's being acknowledged as a problem (or at least a documentation problem) – there's a lot of people who've never heard of anyhow and their stance is just "ugh, error handling is a PITA".
- codeflo 6y agoNot having something as basic as this in the std is also a cause of painful incompatibilities and instabilities. Before anyhow became established, the ecosystem seemed to standardize on the failure crate, which was for all practical purposes basically the same thing. Making the switch wasn’t hard in my case, but also felt completely pointless.
- octoberfranklin 6y agoIs there a concise statement of the differences (aside from syntax) between failure and anyhow? Like any philosophical difference? I've been using failure and would like to know if I should switch.
- sjustinas 6y agoThere is one in anyhow's README. https://github.com/dtolnay/anyhow#comparison-to-failure https://github.com/dtolnay/anyhow#comparison-to-failure
- 6y ago
- mjw1007 6y agoI see one of the two shepherds for the group is the author of https://github.com/yaahc/eyre https://github.com/yaahc/eyre, which is a fork of `anyhow` that adds features. So I expect that's an indication of what's likely to come out of this.
- codeflo 6y agoI hope that std::prelude::Result becomes anyhow::Result, including the default generic argument (i.e. Result<u32> just works, no need to write Result<u32, UnnecessarilySpecificErrorType> unless you have a reason to). That would encourage examples and simple scripts to use Results and ? instead of calling unwrap and panicking. Not only would this teach better practices earlier, it would also address many of the complaints that Rust’s error handling is harder than exceptions in common usecases.
- danieldk 6y agoI hope that std::prelude::Result becomes anyhow::Result, including the default generic argument (i.e. Result<u32> just works But that's basically giving up on informative library-specific error types. It may a valid solution, but it is quite a big leap. Another tack may be to have the functionality of thiserror in the standard library, to make it less of a hassle to define errors.
- masklinn 6y ago> But that's basically giving up on informative library-specific error types. It may a valid solution, but it is quite a big leap. Technically the stdlib could probably define Result<T, E=Box<dyn Error>> That way there wouldn't strictly be any loss, you'd just get a more convenient "application-level" Result OOTB. I don't know if that would be incompatible with the existing though.
- steveklabnik 6y agoOne serious issue there is that this would no longer be in libcore.
- masklinn 6y agoCouldn't libcore export the current version and std re-export the aliased version with a default? The prelude lives in and uses std, and I don't see people who want exceptions using no_std anyway.
- AlexanderDhoore 6y agoA bit of topic but: I write a lot of C and lately I've been using 'bool' as my error handling type. This probably sounds stupid to many people, but I'm a minimalist. If a function has an error -> return false. In the past I would have returned an enum or just int with values for every error that could occur. But I realized that I almost never actually handle the error cases differently. I just want to know whether an error did or did not happen. This style gives me 99% of what I need, and it's so god damn simple.
- izacus 6y agoThat works until you find out that you just wrote one of those godawful pieces of software that say "An error has occured" for everything that goes wrong, including wrong password, network connection issues or simple misconfiguration.
- kortex 6y agoI agree. This sounds pretty awful. I've worked everything from camera drivers to distributed, and I can't think of a single interface I've used where only having a "Something failed" signal wouldn't drive me up a wall debugging. Opening a file is probably the most mundane thing I can think of, and there's at least two situations I want to log/bubble up - file not found vs some other i/o error. Oh, setters and getters I guess return bool works ok.
- tluyben2 6y agoYep, I was integrating a banking API yesterday and every single error just shows ‘Something went wrong, contact support.’. Nothing more; I asked if this was for security (to log and hide the real error as it might give something away), and their answer was ‘kind of’ as they do not have sane errors, just stacktraces logged to Sentry. Incredibly hard to debug (as client we get no access to their Sentry) as you do not know if the authorisation was wrong or some amount was wrong or whatever. And all errors including not found(404) are 500...
- jeffrallen 6y agoThis is great, but I can't help remembering learning Go 10 years ago, when this was already decided (for better or worse) by the language designers. Languages are for getting stuff done NOW, not discussing getting stuff done better, maybe, someday.
- masklinn 6y agoYou… do realise that you can get stuff done with rust NOW, and this is simply a project to make things better, right? Like, I don't know, discussions around generics in some future Go? Hell, 4 out of the 5 goals stated in this announcement aren't even about changing anything but rather about mapping out the existing landscape and clarifying the current best practices, which by definition are already in use.
- jeffrallen 6y agoI am also not in favor of Go generics.
- masklinn 6y agoBut again this has nothing to do with the article, or the thread. Discussing possible evolutions doesn't preclude "getting stuff done NOW".
- vbezhenar 6y agoDespite all modern trends to use return value rather than exception I still think that exceptions are vastly superior approach to error handling. For me the ideal implementation is Java without checked exceptions. Rust's panics are similar to exceptions, so for me the ideal implementation should include some short syntax to allow converting from Result to result value and throw panic for errors, but also allow hierarchy of panics and try-catch handling of those panics (so it would throw some specific panic, not just common panic).
- FiberBundle 6y ago> the ideal implementation should include some short syntax to allow converting from Result to result value and throw panic for errors As far as I know rust implements that with the '?' syntax.
- masklinn 6y agoNo. `?` will short-circuit the Err case, possibly converting an error value, but it doesn't panic. `foo?` desugars to[-1]: match foo { Ok(v) => v, Err(e) => return From::from(e) } What "converts a Result into a value and panics on error" is Result::unwrap[0], or Result::expect[1] (for a custom panic message). [-1] as of rustc 1.46 anyway, I don't know how "try blocks" will effect `?`, though possibly the combination of "try blocks" and unwrap would make it easier to just panic an entire function: currently each Result site has to be unwrapped by hand whereas otherwise you could just `?` every site normally and unwrap the entire block I guess? [0] https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap https://doc.rust-lang.org/std/result/enum.Result.html#method... [1] https://doc.rust-lang.org/std/result/enum.Result.html#method.expect https://doc.rust-lang.org/std/result/enum.Result.html#method...
- masklinn 6y ago> Despite all modern trends to use return value rather than exception I still think that exceptions are vastly superior approach to error handling. Not providing any value judgement but… this seems completely irrelevant to the topic. Rust has long-settled on not exceptions, and they are largely incompatible with the language's stated goals and purpose. > Rust's panics are similar to exceptions Except for the part where their very existence is optional. > so for me the ideal implementation should include some short syntax to allow converting from Result to result value and throw panic for errors You mean like `Result::unwrap` which has existed since before Rust 1.0 was a thing? > but also allow hierarchy of panics and try-catch handling of those panics (so it would throw some specific panic, not just common panic). I don't want to be too forward about it, but that's a language which really has nothing whatsoever to do with Rust: the "hierarchy of panics" alone is completely disqualifying so… I see even less what that comment's doing in here.
- cies 6y agoI applaud Rusts openness wrt the collaboration by which decisions are made.
- rkwz 6y agoFor beginners who are struggling with Rust's error handling, I wrote a post that goes through common use cases such as: * Ignoring the error * Terminating the program * Using a fallback value * Bubble up the error * Bubble up multiple errors * Match boxed errors * Libraries vs Applications * Create custom errors * Bubble up custom errors * Match custom errors http://www.sheshbabu.com/posts/rust-error-handling/ http://www.sheshbabu.com/posts/rust-error-handling/
- amelius 6y agoI'm curious, what did Rust developers think was wrong with exceptions? I'm pretty happy with them in other languages, as they allow me to focus more on the "normal" path in my code.
- steveklabnik 6y agoThe real core of it is that exceptions demand runtime support, and as a systems language, Rust runs in places where that may be prohibitive. Beyond that, there are other reasons, but in my mind, that is the fundamental issue. Everything else kind of rests on top of that.
- throwaway894345 6y agoOut of curiosity, how does C++ get away with it?
- steveklabnik 6y agoAt its core, compilers offer a flag that lets you turn them off, same as Rust. (-fno-exceptions is the moral equivalent of -C panic=abort) Though I believe that's technically a language extension, but IANAC++SL (I Am Not a C++ Standards Lawyer) The language also offers some more language support for declaring that some things will never use exceptions, via noexcept. This matters a bit more because, since the language itself has exceptions as a feature, some things assume that they exist in order to work properly, in my understanding.
- thesz 6y agoI used to say that what is a library in Haskell is a language feature in most other languages. Now I see what is library in Haskell is a "Project Group" in Rust. Rust is much further than most other languages here. Joking aside, why did Rust not adopted type constructors for a start? Having "Error Exception Int" would not let you ignore the error when you try to get that Int. Having "TChan x -> STM x" would not let you perform channel reading outside of transaction with that channel. Monadic binding will not let you mix Error and STM computations easily while preserving sequentiality of code. Even Kotlin got that, for all PL theory sake. The language that is very close to Java. I cannot see why Rust can't. PS And things from this comment [1] would be very much explainable through "follow the types". [1] https://news.ycombinator.com/item?id=24526604 https://news.ycombinator.com/item?id=24526604
- steveklabnik 6y ago> Now I see what is library in Haskell is a "Project Group" in Rust. Sort of, a "project group" is a means for organizing people to help accomplish tasks in the language. (or other parts of the project in general, this group is under the libs team, which maintains the standard library) > Joking aside, why did Rust not adopted type constructors for a start? It is not trivial to adopt advanced type system features in a language like Rust, and we have been working on getting to this point. It just takes a lot of time. Associated type constructors are called "generic associated types" in Rust, and are desired, and being worked on, it takes non-trivial effort. Higher kinded types are less clear. Monadic binding even less clear than that.
- comex 6y agoIs the parent actually talking about associated type constructors? This: > Having "Error Exception Int" would not let you ignore the error when you try to get that Int. just sounds like a description of Result, which of course Rust already has.
- steveklabnik 6y agoNo, I believe that they are talking about higher kinded types, but I was trying to discuss all of our movement in this general direction. ATC gives you a certain amount of what you want out of HKT, in a Rust context, anyway. > just sounds like a description of Result, which of course Rust already has. That would be "Either Exception Int", I believe they were talking about Control.Monad.Error. But maybe you are right!
- ziml77 6y agoI'm very happy to see this. I came back to Rust for a personal project after a long break from using it. The first issue I looked into was the error handling situation. I found a blog post that covered it pretty nicely and led me to the anyhow crate, but I would love for there to be official guidelines and for the features from anyhow and thiserror to be built in.
- tikhonj 6y agoI love the flow that the project laid out: survey what the community does ⇒ figure out pain points ⇒ suggest solutions and consolidate. This ensures suggestions are informed by what already works (or doesn't) and the process has useful outcomes at each step. Even if the project stalls after its initial work, it will have documented the error-handling patterns people use in the wild—legitimately useful by itself.