Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compilation error when deriving with #[from] for a type with an argument containing lifetimes #114

Closed
VanillaBrooks opened this issue Dec 27, 2020 · 1 comment · Fixed by #116

Comments

@VanillaBrooks
Copy link

VanillaBrooks commented Dec 27, 2020

I have a minimal reproducible example of some simplified code that I am using:

#[derive(thiserror::Error, Debug)]
enum Error<'a> {
    #[error("SomeError occured")]
    Foo(#[from] SomeError<&'a str>),
}

#[derive(thiserror::Error, Debug)]
struct SomeError<T: std::fmt::Debug> {
    x: T,
}

impl<T> std::fmt::Display for SomeError<T>
where
    T: std::fmt::Debug,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}", self.x)
    }
}

This seems pretty reasonable, but when compiling I get the error:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
 --> src/main.rs:5:10
  |
5 | #[derive(thiserror::Error, Debug)]
  |          ^^^^^^^^^^^^^^^^
  |
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 6:12...
 --> src/main.rs:6:12
  |
6 | enum Error<'a> {
  |            ^^
note: ...so that the types are compatible
 --> src/main.rs:5:10
  |
5 | #[derive(thiserror::Error, Debug)]
  |          ^^^^^^^^^^^^^^^^
  = note: expected `&Error<'_>`
             found `&Error<'a>`
  = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `SomeError<&str>` will meet its required lifetime bounds
 --> src/main.rs:7:13
  |
7 |     #[error("SomeError occured")]
  |             ^^^^^^^^^^^^^^^^^^^
  = 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

This error is pretty tricky to decipher. It also does not occur when the type argument to SomeError<T> does not contain a lifetime (SomeError<String> works fine). The error can be solved by removing the #[from] and instead implementing From manually:

impl<'a> From<SomeError<&'a str>> for Error<'a> {
    fn from(x: SomeError<&'a str>) -> Self {
        Error::Foo(x)
    }
}

For posterity I will include the results of cargo-expand. The error above mentions that SomeError<&str> does not meet the lifetime bounds somewhere (likely in the From impl), but ctrl + F shows that this type does not appear anywhere (which adds to my confusion).

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std;
fn main() {}
enum Error<'a> {
    #[error("SomeError occured")]
    Foo(#[from] SomeError<&'a str>),
}
#[allow(unused_qualifications)]
impl<'a> std::error::Error for Error<'a> {
    fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
        use thiserror::private::AsDynError;
        #[allow(deprecated)]
        match self {
            Error::Foo { 0: source, .. } => std::option::Option::Some(source.as_dyn_error()),
        }
    }
}
#[allow(unused_qualifications)]
impl<'a> std::fmt::Display for Error<'a> {
    fn fmt(&self, __formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        #[allow(unused_variables, deprecated, clippy::used_underscore_binding)]
        match self {
            Error::Foo(_0) => __formatter.write_fmt(::core::fmt::Arguments::new_v1(
                &["SomeError occured"],
                &match () {
                    () => [],
                },
            )),
        }
    }
}
#[allow(unused_qualifications)]
impl<'a> std::convert::From<SomeError<&'a str>> for Error<'a> {
    #[allow(deprecated)]
    fn from(source: SomeError<&'a str>) -> Self {
        Error::Foo { 0: source }
    }
}
#[automatically_derived]
#[allow(unused_qualifications)]
impl<'a> ::core::fmt::Debug for Error<'a> {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match (&*self,) {
            (&Error::Foo(ref __self_0),) => {
                let mut debug_trait_builder = f.debug_tuple("Foo");
                let _ = debug_trait_builder.field(&&(*__self_0));
                debug_trait_builder.finish()
            }
        }
    }
}
struct SomeError<T: std::fmt::Debug> {
    x: T,
}
#[allow(unused_qualifications)]
impl<T: std::fmt::Debug> std::error::Error for SomeError<T> {}
#[automatically_derived]
#[allow(unused_qualifications)]
impl<T: ::core::fmt::Debug + std::fmt::Debug> ::core::fmt::Debug for SomeError<T> {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match *self {
            SomeError { x: ref __self_0_0 } => {
                let mut debug_trait_builder = f.debug_struct("SomeError");
                let _ = debug_trait_builder.field("x", &&(*__self_0_0));
                debug_trait_builder.finish()
            }
        }
    }
}
impl<T> std::fmt::Display for SomeError<T>
where
    T: std::fmt::Debug,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(::core::fmt::Arguments::new_v1(
            &[""],
            &match (&self.x,) {
                (arg0,) => [::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Debug::fmt)],
            },
        ))
    }
}

rustc 1.48.0 (7eac88abb 2020-11-16)
thiserror = "1.0.22"

If someone can point me in the correct direction in the library I'm willing to take a stab at solving this issue.

@dtolnay
Copy link
Owner

dtolnay commented Dec 27, 2020

The borrow checker is correct, that wouldn't be a valid std::error::Error impl.

I put up #116 to produce a better diagnostic for this case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants