7 ms·
Beginner's guide to error handling in Rust
- sbt 4y agoThe problem with errors in Rust is that the stdlib defined Error as a trait and traits are hard to use (not sized, etc). The current state of affairs with thiserror and anyhow is not satisfactory if we want to ease the Rust learning curve. Errors are too fundamental to be this hard.
- kaba0 4y agoI’m not sure there is a way out - this is the fundamental aspect of low level languages. If you want to care about memory allocation, you have to care about it everywhere. Rust is a really great language, but I believe it is a trap to go down this “high-level low-level language” route C++ also chose.
- iratewizard 4y ago
- mlindner 4y agoHow is someone who submitted links only three times since September last year a spammer? Yes they're self-promoting their own blog, but I don't see the problem wit that.
- nindalf 4y agoThat’s not quite it. They definitely had a time in 2020 where they would repeatedly submit links to their website over and over. I can see why they’d do that, because getting to the front page involves luck. But still, not cool.
- littlestymaar 4y agoUnless they've tampered with their submissions history[1], I only see at most two submissions for the same blog post, one day apart. Would I be OK with someone posting the same link over and over half a dozen time, no, but a single retry sounds fair to me. [1]: https://news.ycombinator.com/submitted?id=rkwz https://news.ycombinator.com/submitted?id=rkwz
- iratewizard 4y ago> Please don't use HN primarily for promotion. It's ok to post your own stuff occasionally, but the primary use of the site should be for curiosity 90% of their posts are self promotion and they've done it more than 50 times.
- bluejekyll 4y agoThis is a good intro, while it mentions `thiserror`, I personally can’t recommend this enough. For anyone building a library, the ability to wrap underlying errors and generate a From implementation quickly for them (converts from source error to target) is it’s super power. It also takes a simple error string to create the Display implementation. It does all of this and generates code and types that are consistent with the std library. It’s good to understand the error mechanics, but don’t wast time writing custom errors, just use `thiserror`.
- eptcyka 4y agoFor years, there have been definitive error crates, each with it's own monstrocity of macro to generate more code, and for every new one there have been painful migrations for codebases of any significant size. Writing out the error types manually isn't actually that difficult.
- tempest_ 4y agoFor library code thiserror has been by far the best experience, sure I can write out the error code manually but why would I waste my time pumping out boiler plate. The combination of thiserror and anyhow have been my go to for nearly 3 years without issue.
- stormbrew 4y agothiserror is quite a lot lighter than most of the prior ones have been. It also has less magic than they tended to, which is nice. It just does what it says it's gonna do mostly. Honestly I think thiserror and anyhow strike the right balances that I think they could be candidates for stdlib inclusion, and then might be more optimized.
- littlestymaar 4y ago> and for every new one there have been painful migrations for codebases of any significant size Why did you migrate though? At work, we're still using error_chain for the oldest library we have since the ergonomics of the library isn't an issue for these stable-ish libs. If what you already have works, just don't migrate. It's not like there's likely hidden segfaults or memory vulns waiting there…
- c7DJTLrn 4y agoI found error handling in Rust to be an unergonomic nightmare and is one of the reasons I don't want to write it anymore. Usually people recommend crates designed to make the experience better but that's a failure in my opinion. Every project ends up doing it differently -- anything like that should be in the standard library. In Go, you can build massive projects without needing any dependencies to make error handling ergonomic.
- ParetoOptimal 4y ago> In Go, you can build massive projects without needing any dependencies to make error handling ergonomic. Because it's always unergonomic?
- nindalf 4y agoI was with you until the end. Go errors leave something to be desired - it’s not easy to wrap, compare errors or print stack trace. These can be fixed with the right library and conventions, but the default error handling is only good enough for small and medium projects. (In my subjective opinion). Agree that Rust errors need to be better. anyhow and thiserror are fine libraries but I want that experience out of the box, ideally. There are ongoing efforts to improve things, so I’m hopeful.
- c7DJTLrn 4y ago>it’s not easy to wrap fmt.Errorf("... %w", err) >compare errors errors.Is(err, ErrorType)
- deleted 4y ago[deleted]
- nemothekid 4y agoExcept when you do (1), your "wrapped" error can longer be compared. You end up with the same Rust-style boiler plate of building your own error structs or you eschew the ability to inspect your error types at all. For example, if I have some library that makes a network request and that request times out do I: 1. Return net.Error? 2. fmt.Errorf? 3. My own custom type The problem with (1), is that API is invisible; the caller is now expected to trawl through my code or documentation for any errors I might return that the caller might want to handle. (2) prevents the caller for acting on my error without string mangling (and again has to inspect my code for the exact strings to handle. (3) is the best, but has the same level of boilerplate; and is also, I've found, unidiomatic in Go. The second blight of Go's error handling situation (after if err == nil), is errors are rarely part of an APIs contract; especially outside of the standard library. Often times if you want to handle a special error code for a given API it means actually opening up the source code and hunting down where that error is thrown and hoping that (1) the error type is public or (2) that the library author won't break your string mangling in a later version.
- Someone 4y agoFTA: let result = reqwest::blocking::get(url); let response = match result { Ok(res) => res, Err(err) => return Err(err), }; Is that truly idiomatic? I would think the Err(err) => return Err(err) line needlessly constructs a copy of result. Or is that necessary because of the borrow checker? (Also, for those unfamiliar with it: reqwest is not a typo. See https://github.com/seanmonstar/reqwest https://github.com/seanmonstar/reqwest)
- richardwhiuk 4y agoThe idiomatic way to write this would be: let response = reqwest::blocking::get(url)?; In the way written there's no additional copy.
- Ambroisie 4y agoAs pointed out, the idiomatic way to do this is to use the try operator (`?`). But to answer your question about copies : Rust is a move-only language, copies are actually called `.clone()`, except for a few types which are cheap enough to copy that they implement the `Copy` trait. So in the code you quoted, the match is done on the value of `result` (notice that there is not borrowing/`&` operator). The match arm `Err(err)` moves `err` out of `result` and returns it. Obviously, the compiler will optimize away all those moves, it's as if they did not exist.
- littlestymaar 4y ago> But to answer your question about copies : Rust is a move-only language, copies are actually called `.clone()`, except for a few types which are cheap enough to copy that they implement the `Copy` trait. This is almost true, but not exact. Copy is for everything where cloning is just memcopy, that doesn't means it's necessarily cheap ([42;4_000_000] implements Copy, yet it's not cheap to copy at all…). And moving things sometimes (but not always) means the thing is getting memcopied (or it could use a pointer, depending on the optimizer's m̶o̶o̶d̶ euristics)
- throwaway234232 4y ago> I would think the Err(err) => return Err(err) line needlessly constructs a copy of result It sounds like this is coming from a C++ bias? So please forgive me if this is wrong. Rust, in my experience, favors move semantics first, then copy semantics after. I know in C++, we had implicit copy constructors, with move semantics after with rvalue references, where you need to use `std::move` in a lot of cases. So what helps, in my opinion, is to think of Rust as using `std::move` as a default.
- PointyFluff 4y ago?;
- OJFord 4y agoWhy isn't `Result<T, E>` just `Result<T, E> where E: std::Error`? I love rust, but I hate 'no from<lib::Error> implementation for my::Error' et al. 99.99% of the time I just want to bubble up a string to print from anything I'm using that might error. I've used anyhow and friends, it just still requires more from me than I think should necessary. But this is an honest question, several people put more thought into it than I have and decided it should be so, why?
- steveklabnik 4y agoOne good reason to not do this is it's std::error::Error, but there is no corresponding core::error::Error. no_std needs to be able to use Result too.
- deleted 4y ago[deleted]
- OJFord 4y agoThat feels a bit incomplete to me - that explains the language mechanism existing for using something different, but not std's lack of something more specific? As in, std::result could use std::error::Error to specialise core::result's more generic Result? And it's barely any extra work at all for a maintainer of a crate with a `no_std` feature, since you can still use the same Result; it's no longer required that its Error type implements std's, but it can?
- steveklabnik 4y agoRust doesn't yet have specialization. It is used internally by the standard library, but only for optimizations, as far as I know.
- kccqzy 4y agoThere are plenty of cases where the Err alternatives of Result doesn't actually represent an error, just a case to be handled differently. For example the binary search function (https://doc.rust-lang.org/std/primitive.slice.html#method.binary_search https://doc.rust-lang.org/std/primitive.slice.html#method.bi...) returns an index of the insertion position when the search fails. That's not usually an error to be surfaced. I find that this distinction is basically the application vs library distinction in the article. Application developers tend to "just want to bubble up a string" whereas library developers tend not to.