Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lifetime error using associated type of trait with lifetime parameter

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.

like image 675
Craig M. Brandenburg Avatar asked Dec 26 '16 22:12

Craig M. Brandenburg


1 Answers

In use_alpha, you have used the same lifetime for the reference to Alpha and its lifetime parameter. Its lifetime parameter then becomes the lifetime of Impl's Trait::T. The note gives a hint about the order that values are dropped: Impl::T gets dropped before Impl because it's part of Impl's definition, but that means that some parts of Alpha have already been dropped while it is still around.

You can fix this by using two lifetimes parameters in use_alpha:

fn use_alpha<'a, 'b>(_: &'a Alpha<'b, Impl>) {}

This will allow the compiler to infer different lifetimes for each of the types.

like image 53
Peter Hall Avatar answered Oct 07 '22 23:10

Peter Hall