Understanding "does not live long enough" error


#1

When I compile the code below, rustc complains:

error[E0597]: `vec` does not live long enough
  --> longenough.rs:14:19
   |
14 |     let vec_ref = vec.iter_mut().map(|b| &mut **b).collect();
   |                   ^^^ borrowed value does not live long enough
15 |     f(vec_ref);
16 | }
   | - `vec` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created

However I don’t understand what this is complaining about. Would anyone be able to give some hint?

One thing that confuses me is that changing the uses of dyn Tr with almost anything else (like T or i32) seems to cause the error to disappear. Also I can work around this issue by using a newtype wrapper around &'a mut dyn Tr.

Here is the code:

struct T {}
trait Tr {}
impl Tr for T {}
impl Drop for T {
    fn drop(&mut self) {
        println!("dropping T")
    }
}

fn f(_: Vec<&mut dyn Tr>) {}

fn main() {
    let mut vec: Vec<Box<dyn Tr>> = vec![Box::new(T {})];
    let vec_ref = vec.iter_mut().map(|b| &mut **b).collect();
    f(vec_ref);
}

playground link: https://play.rust-lang.org/?gist=5e2b0f08e456e14cabb433ffb047b5b0&version=stable&mode=debug&edition=2015


#2

If you replace f with:

fn f(_: Vec<&mut (dyn Tr + '_)>) {}

then it seems to work. I’ve never really understood how lifetime elision works with trait objects but based on trial and error it seems the original f is an elided version of:

fn f<'a>(_: Vec<&'a mut (dyn Tr + 'a)>) {}

#3

Thank you, I didn’t realize that there was a hidden lifetime bound on trait object types. The rules in the reference seem to give what you described: https://doc.rust-lang.org/beta/reference/lifetime-elision.html

However, I still don’t understand why this is an error. The type of f seems to say that the concrete type of the dyn object must outlive the reference, and indeed it does. So what is the problem…?

Also I found something weird: I can make the code compile (without changing the type of f) by changing the definition of vec_ref to:

let vec_ref = vec.iter_mut().map(|b| {let c: &mut dyn Tr = &mut **b; c} ).collect();

i.e. just introducing an intermediate variable with a type annotation. Furthermore, Rust 2018 on the playground seems to accept this:

let vec_ref = vec.iter_mut().map(|b| { &mut **b } ).collect();

i.e. just with extra curly braces around the expression.

How are these possible?

playground links:


#4

You can alternatively get it working with;

let vec_ref = vec.iter_mut().map(|b| &mut **b as &mut dyn Tr).collect();

The problem is that &mut **b has type &mut (dyn Tr + 'static)
'static is the default lifetime bound from Boxing.


#5

I think this is the same issue/manifestation as https://github.com/rust-lang/rust/issues/42473


#7

Thank you @jonh and @vitalyd, I think I understand it now. It’s a rather delicate interaction between type inference and implicit coercion!