Still struggling with my errors … I have an error enum where I’d like to use one variant for a ‘catch-all’ From implementation that should make the ? operator work with all errors.
Is this possible at all? This doesn’t compile:
use std::error::Error;
#[derive(Debug)]
enum MyError {
A, B, C,
Catchall(Box<dyn Error + Send + Sync>),
}
impl std::fmt::Display for MyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "my error")
}
}
impl Error for MyError {}
impl<E> From<E> for MyError
where
E: Error + Send + Sync + 'static,
{
fn from(error: E) -> Self {
Self::Catchall(error.into())
}
}
error[E0119]: conflicting implementations of trait `std::convert::From<MyError>` for type `MyError`:
--> src/lib.rs:17:1
|
17 | / impl<E> From<E> for MyError
18 | | where
19 | | E: Error + Send + Sync + 'static,
20 | | {
... |
23 | | }
24 | | }
| |_^
|
= note: conflicting implementation in crate `core`:
- impl<T> std::convert::From<T> for T;
Why is this? If this catch-all really can’t be made to work, what would be the alternatives?
That's because MyError implements Error itself, so in impl From<E: Error> for MyError, it's possible to substitute E = MyError, which in turn clashes with the impl From<T> for T blanket impl defined in the standard library.
A workaround could be to define a new trait and implement it for all other error types you want to work with, except MyError.
To give a bit of context: I’m doing a callback-based API where functions may return Result<T, MyError>.
Those functions will ultimately be written by someone else. The idea was to have a ‘catchall’ implementation such that the ? would work with whatever error users have to deal with in their function bodies.
error[E0277]: `?` couldn't convert the error to `MyError`
--> src/lib.rs:27:48
|
27 | let _ = std::fs::File::open("blablablabla")?;
| ^ the trait `std::convert::From<std::io::Error>` is not implemented for `MyError`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= note: required by `std::convert::From::from`
Typically, that is considered bad practice and libraries don't do that. Instead, the user of the library has to ensure they provide conversions from their own error types.
Either map_err for ad-hoc cases, but what I meant by "providing the conversion" is that they are expected to implement From on their own error types, in general.
Yes, Anyhow is great for providing a catch all for applications or CLI's that need to be able to catch every different kind of error and handle it appropriately without panicking.
For libraries it is best practice to implement From for all of the possible errors that your library will run into from the libraries that it uses. For example, if your library does IO and and you need to handle std::io::Error in your code. You want to impl From<std::io::Error> for MyError. Once you do that, in your library code you can do something like this:
fn do_something() -> Result<(), MyError> {
// Here we are able to use the `?` operator to propagate an std::io::Error
// because we have implemented `From<std::io::Error>` for `MyError`.
let value = function_that_may_have_io_error()?;
}
That way any user of your library will have a concrete error type returned by your functions that they an match on and make sure they handle appropriately.