I'm trying to implement MyError
that can:
anyhow::Error
-> MyError
.std::error::Error
-> MyError
.MyError
-> anyhow::Error
.Here’s my current code:
#[derive(Debug)]
pub enum MyError {
Anyhow(anyhow::Error),
Custom,
}
impl<E> From<E> for MyError
where
E: std::error::Error + Into<anyhow::Error>,
{
fn from(e: E) -> Self {
MyError::Anyhow(e.into())
}
}
impl From<anyhow::Error> for MyError {
fn from(e: anyhow::Error) -> Self {
MyError::Anyhow(e)
}
}
impl From<MyError> for anyhow::Error {
fn from(err: MyError) -> Self {
match err {
MyError::Anyhow(e) => e,
MyError::Custom => anyhow::anyhow!("CustomError"),
}
}
}
fn main() {
let io_err = std::fs::read("not_exist_file").unwrap_err();
// std::io::Error -> MyError
let my_err_from_std_err: MyError = io_err.into();
// MyError -> anyhow::Error
let anyhow_err_from_my_err: anyhow::Error = my_err_from_std_err.into();
// anyhow::Error -> MyError
let my_err_from_anyhow: MyError = anyhow_err_from_my_err.into();
}
When I run this, I get the following error:
conflicting implementations of trait `From<anyhow::Error>` for type `MyError`
--> src/main.rs:16:1
|
7 | / impl<E> From<E> for MyError
8 | | where
9 | | E: std::error::Error + Into<anyhow::Error>,
| |_______________________________________________- first implementation here
...
16 | impl From<anyhow::Error> for MyError {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyError`
|
= note: upstream crates may add a new impl of trait `std::error::Error` for type `anyhow::Error` in future versions
The issue seems to be a conflict between my implementation of From<anyhow::Error>
and the fact that anyhow::Error
already implements std::error::Error
, which causes a collision.
I've been struggling with this issue for a whole day now and it is starting to feel like impossible to achieve.
Perhaps if I could just implement std::error::Error
for MyError
, could this would resolve the conflict?. If that's the case, I'd be open to a solution where MyError
implements std::error::Error
itself and allows for conversions to and from anyhow::Error
.
Edit:
This solution comes from the author of anyhow and it works like a charm
impl<E> From<E> for MyError
where
- E: std::error::Error + Into<anyhow::Error>,
+ E: Into<anyhow::Error>,
+ Result<(), E>: anyhow::Context<(), E>,
{
fn from(e: E) -> Self {
MyError::Anyhow(e.into())
}
}
-
- impl From<anyhow::Error> for MyError {
- fn from(e: anyhow::Error) -> Self {
- MyError::Anyhow(e)
- }
- }
In this case, the compiler is your friend. The answer is in the error message:
note: upstream crates may add a new impl of trait `std::error::Error` for type `anyhow::Error` in future versions
The problem is, you have these two impl
blocks:
impl<E> From<E> for MyError
where
E: std::error::Error + Into<anyhow::Error>,
{
fn from(e: E) -> Self {
MyError::Anyhow(e.into())
}
}
impl From<anyhow::Error> for MyError {
fn from(e: anyhow::Error) -> Self {
MyError::Anyhow(e)
}
}
Now consider what happens when you try to convert anyhow::Error
into MyError
. The compiler sees the first block and tries to match: anyhow::Error
implements Into<anyhow::Error>
(any type T
trivially implements Into<T>
), but doesn't implement std::error::Error
, so we should be fine here (spoiler alert: we aren't, but we'll get back to this later). It also sees the second block, where we have From<anyhow::Error>
, so it obviously applies and the compiler can use it.
However, you don't have any guarantee that the anyhow
crate maintainers won't introduce an implementation for std::error::Error
for anyhow::Error
in the future. This sort of change only needs a minor SemVer bump, so your code could suddenly stop compiling without even changing your Cargo.toml
. Rust compiler tries to prevent this and doesn't allow you to build your program, even though there's technically nothing wrong with it as of today.
What can you do with it? Well, sadly not that much. Your options are:
From<E>
implementation and implementing From
for all the error types you want to support (possibly using macros) — your code stops being genericstd::error::Error
bound on From<E>
and removing From<anyhow::Error>
block — requires you to remove your impl From<MyError> for anyhow::Error
block because of conflicting blanket implementation of impl<T> From<T> for T
Into<anyhow::Error>
bound on From<E>
and using std::error::Error + Send + Sync + 'static
instead while removing the From<anyhow::Error>
— you won't be able to convert from anyhow::Error
to MyError
.There is a possibility this problem will be easier to tackle in the future using specialization, but it doesn't look like it will be available in stable Rust anytime soon.
See also: How do I work around the "upstream crates may add a new impl of trait" error?