dtolnay / thiserror

derive(Error) for struct and enum error types

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Cannot derive on enum with variants wrapping associated types

SuperFluffy opened this issue · comments

Using the most recent thiserror, I am getting an error when trying to compile the following example:

use thiserror::Error;

trait AssocError {
    type Error: std::error::Error;
}

#[derive(Error, Debug)]
pub enum MyExpandedError<A>
where
    A: AssocError,
{
    #[error("My Foo error")]
    Foo,
    #[error("Error from associated type")]
    Assoc(#[from] A::Error),
}

And this is the error:

error[E0119]: conflicting implementations of trait `std::convert::From<MyExpandedError<_>>` for type `MyExpandedError<_>`:
 --> src/lib.rs:7:10
  |
7 | #[derive(Error, Debug)]
  |          ^^^^^
  |
  = note: conflicting implementation in crate `core`:
          - impl<T> std::convert::From<T> for T;
  = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to previous error

For more information about this error, try `rustc --explain E0119`.
error: could not compile `thiserror_from`.

The compiler's message is correct here, you have a conflict with the From impl in std. You're asking to generate impl<A> From<A::Error> for MyExpandedError<A> where A: AssocError, but there might be a type A for which A::Error = MyExpandedError<A>, so your impl overlaps with impl From<T> for T.

If you didn't intend for a From impl, you should use #[source] instead of #[from]. Something like:

use std::fmt::{self, Debug};
use thiserror::Error;

pub trait AssocError {
    type Error: std::error::Error + 'static;
}

#[derive(Error)]
pub enum MyExpandedError<A>
where
    A: AssocError,
{
    #[error("My Foo error")]
    Foo,
    #[error("Error from associated type")]
    Assoc(#[source] A::Error),
}

impl<A> Debug for MyExpandedError<A>
where
    A: AssocError,
{
    fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
        ...
    }
}

Good point, thank you. I didn't realize this at the time that doing this I could in principle create a recursive type definition.

I am a bit sad because not being able to impl<A> From<A::Error> for MyExpandError<A> where A: AssocError means that I cannot apply the ? operator. :-(