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?