I had my own error type, with lots of conversions to it. It was too non-standard and didn't do error propagatin, so I started to convert to "anyhow". (Good choice?)
So I tried this conversion, modeled on something I was doing with my own error type. I want to be able to convert errors of the form Result<something, &'static str>. But Rust does not allow this conversion.
use anyhow;
impl std::convert::From<&'static str> for anyhow::Error {
fn from(msg: &'static str) -> Self {
anyhow::anyhow!("{}",msg)
}
}
Error message is:
error[E0119]: conflicting implementations of trait `std::convert::From<&'static str>` for type `anyhow::Error`:
--> src/common/errors.rs:17:1
|
17 | impl std::convert::From<&'static str> for anyhow::Error {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `anyhow`:
- impl<E> From<E> for anyhow::Error
where E: std::error::Error, E: Send, E: Sync, E: 'static;
= note: upstream crates may add a new impl of trait `std::error::Error` for type `&'static str` in future versions
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> src/common/errors.rs:17:1
|
17 | impl std::convert::From<&'static str> for anyhow::Error {
| ^^^^^--------------------------------^^^^^-------------
| | | |
| | | `anyhow::Error` is not defined in the current crate
| | `str` is not defined in the current crate
| impl doesn't use only types from inside the current crate
|
= note: define and implement a trait or new type instead
So is it possible to define an implicit conversion from str to anyhow::Error?
Yes. where my code generates the error. But where something being called is returning Result<something, &'static str>, and the enclosing function wants Result<something, anyhow::Error>, conversion is needed. How do I set up such conversions?
OK, that works. Not as elegant as I'd hoped, but not too hard to convert.
Rust needs a unified error story. Python went through this. In early Python, errors could be any type and were usually strings. Then Python got a standard error hierarchy and all errors had to be subclasses of items in the official hierarchy. It helped.
After a few unsuccessful attempts, I'm now starting to appreciate thiserr.
At first it felt intimidating. Possibly because I felt like I needed to define every error I might run into up front. In my most recent attempt (in progress), I started with thiserr's example and will change/add errors as I encounter the need for them. Having an error.rs file helps me flip back and forth easily
I've also used more than one enum. One enum per
important module/struct seems to be working ok. Now I can match on a specific error if I like, or just use anyhow in an application. The specific errors help with debugging while writing the lib! I have so far caught one error by testing which errors my code produced and getting a surprise!
I have to say having witnessed (from afar) the churn in the error handling story since error-chain and failure, I'm happy to stick with anyhow and thiserr for a good while. The error trait has been improved in this time and I think we're in a good place now.
anyhow seems fine. It's the lack of standardization that causes problems, not the functionality. If all library crates that used Result used the same error type, or at least something that converted to a standard error type, life would be easier.
This is one of the two things I've encountered in Rust that come in too many incompatible versions. The other is small vectors and matrices, 2D, 3D, and 4D. I'm using external crates which force me to have three different incompatible sets of those. A headache for game and graphics work.
This is what the std::error::Error trait is for: everybody implements Error for their error types, and you can use dyn Error as a catch-all type that any library error can be converted into. anyhow itself is just a wrapper around Box<dyn Error> that makes it simpler to use directly.
(A library that uses &'static str for its errors is not respecting this convention.)