From trait impl conflit

I m trying to create my own AppError type, but got impl conflit when i impl these two.

Anybody can help this out?
Thanks a lot!

impl<E> From<E> for AppError
    where E: Into<anyhow::Error> {
    fn from(value: E) -> Self {
        AppError::InternalServerError(value.into())
    }
}

impl From<&'_ str> for AppError{
    fn from(value: &'_ str) -> Self {
        AppError::new(value)
    }
}

(I put the code into the Rust Playground in order to see the error).

The error you get is:

error[E0119]: conflicting implementations of trait `From<&str>` for type `AppError`
  --> src/lib.rs:19:1
   |
12 | impl<E> From<E> for AppError
   | ---------------------------- first implementation here
...
19 | impl From<&'_ str> for AppError{
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `AppError`
   |
   = note: upstream crates may add a new impl of trait `std::error::Error` for type `str` in future versions

For more information about this error, try `rustc --explain E0119`.

The reason you get this error is that there may, in the future, be an implementation impl From<&'_ str> for anyhow::Error. This automatically causes the constraint in your where clause &'_ str: Into<anyhow::Error> to be met by this implementation (because there's a blanket impl<F, T> Into<T> for F where T: From<F>).

If that happens, Rust has two implementations to choose from for From<&'_ str> - does it choose From<E> where E: Into<anyhow::Error>, or does it choose From<&'_ str>? Today, the rule is that it can't choose, and instead we have rules (coherence rules) which stop you having two implementations of the same trait that could overlap.

In future, it's possible that we might get something called "specialization" to work soundly, at which point the rules will be relaxed to allow for the sound subset of specialization; but that's a long way off, and there's no guarantee that it'll relax the rules in a way that you want (because we don't yet know what subset of specialization will turn out to be sound, if any).

Who knows if specialization ever comes; but we don’t need as much here. Some form of negative implementations would also help as a Rust language feature to resolve this situation. If the fact that &str does not implement Error was known to the compiler (in a way that could be relied upon during overlap checks) then something like

impl<E> From<E> for AppError
    where E: std::error::Error + Send + Sync + 'static {
    fn from(value: E) -> Self {
        AppError::InternalServerError(value.into())
    }
}

impl From<&str> for AppError{
    fn from(value: &str) -> Self {
        AppError::new(value)
    }
}

would work.

1 Like

Understood now!
Thanks for your elaboration!

don't exactly know what does impl !Error mean here.
Is that means &str does not impl Error but it is unknown to compiler?

As far as I understand, the impl !Trait for Type syntax is some unstable syntax for making sure that a trait is not implemented by a type. I’ve seen it used for op-out out of auto traits, so e.g. PhantomPinned uses it as a starting point for types that don’t implement Unpin.

It’s an unstable language feature though, so the specific syntax is possibly subject to change, and it’s only used as an implementation detail in the standard library. The precise effects of the !Error for &str implementation are actually not clear to me. I haven’t dug deeper to figure out why that impl even exists in the first place and what effects it might have. Without this negative impl, the trait Error wouldn’t be implemented for &str either, so it seems possibly redundant. Also, as far as I can tell it does not have the kind of – IMO desired – effect that a proper “negative impls” feature should have for overlap detection.

I linked the impl !Error for &str nonetheless, because it illustrates nicely not only that &str does not implement Error, but furthermore, at least if I understand the intention correctly, that it’s also not doing so on purpose and we will (probably) never get such an implementation in the future either.

1 Like

See...

A negative impl carries the same semver burden as a positive impl. As such, it's not the same as the lack of an implementation, where you reserve the right to implement a trait later; coherence won't do negative reasoning across crates without the explicit negative impl.

I didn't find an explicit tracking issue for (or even mention of) the feature gate for allowing coherence to consider negative impls, but after some poking around it looks like it's with_negative_coherence.

3 Likes

supposedly specification is considered a replacement with negative impl?
As it seems negative impl is purposed for few years already but no formally defined semantics were found. it's gonna to be a long term till we can avoid these impl in stable ver.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.