Search code examples
rusttraitsgeneric-associated-types

Generic associated type may not live long enough


Take the following example (Playground):

#![feature(generic_associated_types)]
#![allow(incomplete_features)]

trait Produce {
    type CustomError<'a>;

    fn produce<'a>(&'a self) -> Result<(), Self::CustomError<'a>>;
}

struct GenericProduce<T> {
    val: T,
}

struct GenericError<'a, T> {
    producer: &'a T,
}

impl<T> Produce for GenericProduce<T> {
    type CustomError<'a> = GenericError<'a, T>;

    fn produce<'a>(&'a self) -> Result<(), Self::CustomError<'a>> {
        Err(GenericError{producer: &self.val})
    }
}

The GenericError has a lifetime to allow it to take Produce as a reference. However, this code doesn't compile. It gives me the error:

error[E0309]: the parameter type `T` may not live long enough
  --> src/lib.rs:19:5
   |
18 | impl<T> Produce for GenericProduce<T> {
   |      - help: consider adding an explicit lifetime bound...: `T: 'a`
19 |     type CustomError<'a> = GenericError<'a, T>;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds

This error makes sense to me because the GenericError doesn't have any restriction telling it that T must be 'a. I'm having trouble figuring out how to solve the problem though. Perhaps my generic lifetimes are misplaced?

The feature of the trait I wish to capture is that any Produce::CustomError should be able to capture self in a return. Perhaps I am going about this in the wrong way?


Solution

  • The same trait without generic_associated_types takes the lifetime in the trait itself.

    trait Produce<'a> {
        type CustomError;
    
        fn produce(&'a self) -> Result<(), Self::CustomError>;
    }
    
    struct GenericProduce<T> {
        val: T,
    }
    
    struct GenericError<'a, T> {
        producer: &'a T,
    }
    
    impl<'a, T: 'a> Produce<'a> for GenericProduce<T> {
        type CustomError = GenericError<'a, T>;
    
        fn produce(&'a self) -> Result<(), Self::CustomError> {
            Err(GenericError{producer: &self.val})
        }
    }
    

    This tells the impl upfront that T that it must be 'a.