this post was submitted on 11 Jun 2026
33 points (97.1% liked)
Rust
8060 readers
17 users here now
Welcome to the Rust community! This is a place to discuss about the Rust programming language.
Wormhole
Credits
- The icon is a modified version of the official rust logo (changing the colors to a gradient and black background)
founded 3 years ago
MODERATORS
you are viewing a single comment's thread
view the rest of the comments
view the rest of the comments
Rust's explicitness and being forced to handle every case is so incredibly frustrating but also feels so right.
Thanks for posting this. I'm still trying to figure out the best technique to error handling in my programs. I just learned about
anyhowthe other day and kind of starting to work that in.I'm so tired of having to reshape errors with match statements.
This pattern is an annoying habit I keep finding myself repeating.
Check out https://docs.rs/thiserror/latest/thiserror/. You can do this:
And then you can just do
foo(a, b)?because there's now aFromimpl forMyErrorfromLibraryError.You now get a proper error chain via
source(). You can use this to have a nice report at the top level using https://docs.rs/thiserror-ext/0.3.0/thiserror_ext/struct.Report.html that looks like this:I'm playing with this pattern, but it seems like it forces you to have a variant for every upstream error. For instance, I have this
DatabaseError:But I might have 2 or 3 error types that turn into
DatabaseError::Serialize. Then I end up doing customFromimpls. Then I realize serde might beSerializeorDeserialize, so I'm back where I started.Though I would like to have that chain of errors, but I haven't figured out how to do the custom
impl Fromyet (or if it ends up being useful in my case, even).Thanks for the ideas.
For multiple types turning into one variant: you can either impl From for the others yourself, or you can have another enum with variants for them and have thiserror generate the From impl. I usually have lots of enums, at least one per module, sometimes even one per function, that form a tree. The errors you design should relate to the action that was being tried, and only encapsulate the lower error as context information.
I see you're using strings. You can do that, but then you'll lose control over formatting, and additional context info that might be contained in them.
Designing errors isn't trivial and requires some experience. Try some things and over time you'll see what works best.
You can also check out https://docs.rs/snafu/latest/snafu/guide/index.html, another great library - their docs contain excellent guidance on designing error types.
I just haven't figured out how to jam multiple error types into one enum variant yet. There's no reason for the consumer of the module to get more pointless variants, so I'm not really sure I want an enum of enums, but maybe I'm overthinking it. I'll fiddle more and check out your link later today.
I think this is more idiomatic:
Depending on a usecase you might find that many of your "entrypoint functions" might all share the same error type. For example, if you're making an API backend it's likely gonna be something a status code and a message.
In that case a simple way to avoid having to pattern match every single error is to implement
From<DownstreamErrorType> for MyErrorEnum, and just use the?operator. Example:EDIT: this is mentioned in the article already
It's a little verbose (like most of Rust), but I really like this approach. It'll make a lot of logic easier to reason about. I missed that in the article, so thanks for pointing it out.
Well, is it really? The verbose part is converting from an error type to another, which you would likely also do in a language with exceptions in the catch block, or in some giant cursed interceptor thing that does a switch/match on the error type (something I had the displeasure to witness with my own two eyes, multiple times)
(Didn't read the article.)
It's not a secret approach or anything.
thiserrorgives you that with a simple#[from]attribute annotation on the relevant error variant on yourErrorenum (which is what yourErrortype should be).In your case, this just works because you're not attaching custom context to your error. Usually, you would want to attach some context, and in that case,
.map_err()would obviously still be needed, and that's fine. This idea of having to write as little code as possible is stupid.Sometimes, attaching context once is sufficient, sometimes it's not. If it's the former, then you can still do
Fromin your bigger error enums which have variants from your smaller error enums (e.g. crate-levelErrortype with variants trivially wrapping module-levelErrortypes).Is it? Not only is it less work, but generally makes the code way easier to reason about. In this case, instead of just seeing simple function calls explaining the logic flow, you visually have to parse all this weird extra cruft that is generally irrelevant to what the block is doing.
Yes, code golf practiced for any reason other than the sheer joy of it is "stupid." It especially doesn't belong in production code. If typing is really so bad, there are any number of local agents that are competent to finish that map fragment for you.
It's not about "sheer joy", nor obfuscation. It's about making code easier to write and easier to grok.
There is the
map_errmethod on Result which simplifies this case. But yes, anyhow is still easier if you don't care that much about the error contents.Completely forgot about this one. Think I still like the other approach (implementing
From) for most places, but I'll keep this one in mind too, thanks.That's what
?is for. If you have to do that manually, something is not working correctly.That only works if everything has the same error types.
Or just implement Into for your error type. ? Works for Into
Don't implement
Into, instead, implementFrom- and you'll getIntofor free