thiserror icon indicating copy to clipboard operation
thiserror copied to clipboard

Parametrized error types causes type evaluation overflows when recursive

Open zannabianca1997 opened this issue 5 months ago • 0 comments

thiserror works well with recursive error types, and this snippet compiles with no error:

use std::error::Error;

use thiserror::Error; // 1.0.63

#[derive(Debug, Error)]
pub enum MyError {
    #[error("Recurse")]
    Recurse(#[source] Box<MyError>),
    #[error("Payload")]
    Payload(&'static str),
}


fn main() {
    let _: &dyn Error = &MyError::Payload("Hello");
}

But when one uses an error that is parametrized over his content, it causes an overflow in type evaluation:

use std::error::Error;

use thiserror::Error; // 1.0.63

#[derive(Debug, Error)]
pub enum MyError<Payload> {
    #[error("Recurse")]
    Recurse(#[source] Box<MyError<Payload>>),
    #[error("Payload")]
    Payload(Payload),
}


fn main() {
    let _: &dyn Error = &MyError::Payload("Hello");
}
   Compiling playground v0.0.1 (/playground)
error[E0275]: overflow evaluating the requirement `Box<MyError<&str>>: std::error::Error`
  --> src/main.rs:15:25
   |
15 |     let _: &dyn Error = &MyError::Payload("Hello");
   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: required for `MyError<&str>` to implement `std::error::Error`
  --> src/main.rs:5:17
   |
5  | #[derive(Debug, Error)]
   |                 ^^^^^ unsatisfied trait bound introduced in this `derive` macro
6  | enum MyError<Payload> {
   |      ^^^^^^^^^^^^^^^^
   = note: required for the cast from `&MyError<&str>` to `&dyn std::error::Error`
   = note: this error originates in the derive macro `Error` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0275`.
error: could not compile `playground` (bin "playground") due to 1 previous error

I can easily implement by myself, but often this is not possible due to too many variants

use std::error::Error;
use std::fmt::{Display, Debug};

#[derive(Debug)]
pub enum MyError<Payload> {
    Recurse(Box<MyError<Payload>>),
    Payload(Payload),
}

impl<Payload> Display for MyError<Payload> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>)->std::fmt::Result {
        match self {
            Self::Recurse(_) => write!(f, "Recurse"),
            Self::Payload(_) => write!(f, "Payload"),
        }
    }
}

impl<Payload> Error for MyError<Payload>
where 
    MyError<Payload>: Debug + 'static,
{
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            Self::Recurse(source) => Some(source),
            Self::Payload(_) => None,
        }
    }
}

fn main() {
    let _: &dyn Error = &MyError::Payload("Hello");
}

Is this an hard limitation?

zannabianca1997 avatar Sep 03 '24 16:09 zannabianca1997