top | item 41346659

Guide to Error Handling in Rust

32 points| eventhelix | 1 year ago |howtocodeit.com

25 comments

order

airstrike|1 year ago

That was a pleasure to read. Well written, nicely formatted and good links to further reading topics.

The only missing piece was talking about https://github.com/dtolnay/thiserror which I would expect to be prominently featured given how prevalent it is

And possibly https://github.com/dtolnay/anyhow which is arguably a simpler form of "error handling" but sometimes that's all you need—although probably not in the Finance or Space industries ;-)

bobbylarrybobby|1 year ago

The article does talk about anyhow (maybe it was updated?) but yeah, no thiserror seems like a big omission. thiserror is how you solve the problem of exposing your own error types in your public API without pulling your hair out.

jicea|1 year ago

> Surprisingly, the type wrapped by std::result::Result::Err doesn't need an Error bound:

None of the errors types I create implements the trait Error. I'm just using simple enums with various variants and that's sufficient for me. The crates that I develop are mostly binary crate [1] so implementing the trait Error is maybe more prevalent in libraries crate. Reading the article, it feels a little overkill to me. I like the simple, straightforward approach (the one that you learn in the Book). I don't use anyhow also, I actively try to limit the dependencies I'm using.

[1]: https://github.com/Orange-OpenSource/hurl

hyujwfasdf|1 year ago

If Rust had an equivalent to tuples but for sum types I believe the error ergonomics would be better. This would result in more exact error handling instead of a crate wide error enum that has error states that would never happen. Not to mention the chore of constructing enum wrappers for all error cases.

https://youtrack.jetbrains.com/issue/KT-68296/Union-Types-fo...

https://harelang.org/tutorials/introduction#defining-new-err...

shepmaster|1 year ago

I partially agree. I have been experimenting with creating a lot of small error types (e.g. one per function) because of the benefit of narrowing down the set of possible causes. I also dislike crate-wide errors for the same reason.

However, having an anonymous sum type isn’t a good solution here because there’s nowhere for you to add context to. It’s not useful for your final error to say “permission denied” without the context of a file name, or knowing that you were trying to open configuration file, etc.

Also, depending on your implementation, you either cannot allow multiple errors of the same underlying cause (e.g. two IO errors, one for writing and one for opening) because they are the same type or you have positional error tuples (error index 0 is write, index 1 is open). Neither of those is super ergonomic.

UnquietTinkerer|1 year ago

Oh look, my old friend Checked Exceptions! I knew we would meet again one day :)

In all seriousness, anonymous sum types would be quite nice. It would make it easier to be precise about return values in situations where `Optional` and `Result` are not sufficient.

The problem with checked exceptions (well, one of the problems) is that the full set of error conditions a function might encounter is often _too_ detailed. Encoding them all into the type signature of every function can be a lot of work, and quite brittle to minor internal changes. So most code bases will still need a catch-call error type that gets bubbled up to a generic handler somewhere.

nick__m|1 year ago

I am not a rustefarian so take my opinion on that for what it's worth: i.e. not much. [I tried the language about 5 years ago and I was disturbed by the dbus crate overloading of the dereference operator, to Rust practitioners I ask is &*msg.interface() an axiomatic Rust construct ?]

From what I understand from the article, returning a dyn Error in a Result appears to be about as useful as throwing a new Exception("something bad happened") in java. I am sure it gets better in part two "Structured Rust errors" but this guide on error handling doesn't increase my motivation to give Rust another chance.

shepmaster|1 year ago

Yes, `&*foo` is meaningful. For example, `Vec<T>` becomes `[T]` and then `&[T]`.

Yes, a `Box<dyn Error>` is about the most rudimentary way of returning an arbitrary error, but it’s better than a panic or a sentinel value.

avinassh|1 year ago

side note: (assuming you are the author) could you please add RSS feed to your blog? thank you!

eventhelix|1 year ago

I am not the author. You could request this in the comments section on the page.