Guide to Error Handling in Rust(howtocodeit.com) |
Guide to Error Handling in Rust(howtocodeit.com) |
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 ;-)
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.
Along the way, it makes adding details to your error easier (automatic calls to Into without a closure; optional implicit details).
It combines string-like errors (anyhow) and structured errors (thiserror) in one library.
It has basic command line error formatting that shows the causal chain without duplication.
I don’t use eyre so I don’t feel comfortable comparing to it.
.context(ReticulatingSnafu { spline: 42 })?
Automatically generates structs for typing error contexts instead of just having strings. This forms a chain of causes, each of which contains additional context about what was happening when the error was returned.https://youtrack.jetbrains.com/issue/KT-68296/Union-Types-fo...
https://harelang.org/tutorials/introduction#defining-new-err...
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.
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.
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.
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.
In that crate the &*msg.interface().unwrap() magically become an &str !
The Deref trait is designed for the implementation of custom pointer types. The intention is that it will take a pointer-to-T to a T, not convert between different types. It is a shame that this isn’t (probably cannot be) enforced by the trait definition.
Rust tries to strike a careful balance between explicit and implicit mechanisms, favouring explicit conversions between types. Automatic dereferencing in the dot operator is a case where the ergonomics strongly favour an implicit mechanism, but the intention is that this is limited to degrees of indirection, not conversion between arbitrary types.https://doc.rust-lang.org/std/backtrace/struct.Backtrace.htm...
Having the context selectors called the exact same as the variants was the way the original version of SNAFU worked, but it was also the number one cause of confusion and support requests.
[0]: https://docs.rs/snafu/latest/snafu/derive.Snafu.html#changin...
But I must admit that Cargo and clippy were nice and lifetime are easier to understand than when using std::move in c++ is required vs when it's implicit.
But really what made me abandon learning Rust is Nim. My first Nim program I made a small maze game in about 200 lines.
Also the fact that I now mostly program recreationally since I pivoted from software development to infrastructure ( my title is system analyst) at a research University (absolute job security is really nice). Software development at that institution was getting to political to my taste and I like to play with nice hardware!
For example, the standard library `String` type dereferences to `str` which you can then re-reference to `&str`.
ameliaquining awnsered my question and you modulated my opinion, that pattern is can be idiomatic when the principle of minimal surprises is respected.