I'm getting a lifetime error with Rust 1.14 due to using an associated type, demonstrated by the following two similar programs, the first which compiles without error and the second which has the lifetime error.
Program #1 — compiles without error
trait Trait<'a> {
type T;
}
struct Impl;
impl<'a> Trait<'a> for Impl {
type T = std::marker::PhantomData<&'a ()>;
}
struct Alpha<'a, T: Trait<'a>> {
_dummy: std::marker::PhantomData<(&'a (), T)>,
}
fn use_alpha<'a>(_: &'a Alpha<'a, Impl>) {}
fn main() {
for x in Vec::<Alpha<Impl>>::new().into_iter() {
use_alpha(&x); // <-- ok
}
}
Program #2 — has the lifetime error
trait Trait<'a> {
type T;
}
struct Impl;
impl<'a> Trait<'a> for Impl {
type T = std::marker::PhantomData<&'a ()>;
}
struct Alpha<'a, T: Trait<'a>> {
_dummy: std::marker::PhantomData<(&'a (), T::T)>,
}
fn use_alpha<'a>(_: &'a Alpha<'a, Impl>) {}
fn main() {
for x in Vec::<Alpha<Impl>>::new().into_iter() {
use_alpha(&x); // <-- !error!
}
}
Here's the compile-time error for the second program:
error: `x` does not live long enough
--> src/main.rs:20:5
|
19 | use_alpha(&x); // <-- !error!
| - borrow occurs here
20 | }
| ^ `x` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
Here's the diff for the two programs:
#[derive(Clone)]
struct Alpha<'a, T: Trait<'a>> {
- _dummy: std::marker::PhantomData<(&'a (), T)>,
+ _dummy: std::marker::PhantomData<(&'a (), T::T)>,
}
The only difference is that by changing the first program to use an associated type instead of the type parameter in the struct
definition, a lifetime error occurs. I have no idea why this happens. As far as I can tell, the associated type should not incur any additional lifetime restrictions—it's all just 'a
, but clearly the Rust compiler disagrees.
If I replace iteration in the second program's main
function with simple instantiation, then the lifetime error goes away. That is:
fn main() {
let x = Alpha::<Impl> { _dummy: std::marker::PhantomData };
use_alpha(&x); // <-- ok in both programs
}
I don't understand why iteration is any different than direct instantiation.
In
use_alpha
, you have used the same lifetime for the reference toAlpha
and its lifetime parameter. Its lifetime parameter then becomes the lifetime ofImpl
'sTrait::T
. The note gives a hint about the order that values are dropped:Impl::T
gets dropped beforeImpl
because it's part ofImpl
's definition, but that means that some parts ofAlpha
have already been dropped while it is still around.You can fix this by using two lifetimes parameters in
use_alpha
:This will allow the compiler to infer different lifetimes for each of the types.