3

I'm trying to make a node structure but I have no idea why this won't compile (Rust playground):

trait SomeTrait {}

struct SomeObject<'a> {
    something: &'a dyn SomeTrait,
}
impl<'a> SomeTrait for SomeObject<'a> {}

struct OtherObject {}
impl SomeTrait for OtherObject {}

pub struct Node {
    children: Vec<Box<dyn SomeTrait>>,
}
fn main() {
    let a = vec![OtherObject {}];

    let b: Vec<Box<dyn SomeTrait>> = a
        .iter()
        .map(|d| Box::new(SomeObject { something: d }) as Box<dyn SomeTrait>)
        .collect();

    //But if i comment this it's fine... why?
    Box::new(Node { children: b });
}
error[E0597]: `a` does not live long enough
  --> src/main.rs:17:38
   |
17 |     let b: Vec<Box<dyn SomeTrait>> = a
   |                                      ^ borrowed value does not live long enough
18 |         .iter()
19 |         .map(|d| Box::new(SomeObject { something: d }) as Box<dyn SomeTrait>)
   |                  ----------------------------------------------------------- returning this value requires that `a` is borrowed for `'static`
...
24 | }
   | - `a` dropped here while still borrowed

Why does it say that a is still in use? Shouldn't the other variables be dropped before?

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
Ludvig
  • 637
  • 6
  • 18

2 Answers2

3

The type of Node:

pub struct Node {
    children: Vec<Box<dyn SomeTrait>>,
}

does not capture any lifetime information, even though the specific implementation of SomeTrait, ie SomeObject, carries references. The borrow checker then must infer this lifetime to be 'static, as if you had written:

pub struct Node {
    children: Vec<Box<dyn SomeTrait + 'static>>,
}

You can fix this by expressing that Node might contain non-static references:

pub struct Node<'a> {
    children: Vec<Box<dyn SomeTrait + 'a>>,
}

This allows the borrow checker to track the borrow correctly.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
3

Fixed version:

trait SomeTrait {}

struct SomeObject<'a> {
    something: &'a dyn SomeTrait,
}
impl<'a> SomeTrait for SomeObject<'a> {}

struct OtherObject {}
impl SomeTrait for OtherObject {}

pub struct Node<'a> {
    children: Vec<Box<dyn SomeTrait + 'a>>,
}

fn main() {
    let a = vec![OtherObject {}];

    let b: Vec<Box<dyn SomeTrait>> = a
        .iter()
        .map(|d| Box::new(SomeObject { something: d }) as Box<dyn SomeTrait>)
        .collect();

    Box::new(Node { children: b });
}

So, what's the problem ?

pub struct Node {
    children: Vec<Box<dyn SomeTrait>>,
}

is the same as

pub struct Node {
    children: Vec<Box<dyn SomeTrait + 'static>>,
}

which means (1,2) that the SomeTriat objects must not hold any references that are not 'static. But you have:

.map(|d| Box::new(SomeObject { something: d }) as Box<dyn SomeTrait>)

where |d| is actually a reference that does not live for as long as 'static (it is valid for as long as the vector a is in scope, which is less than 'static), therefore the error message:

returning this value requires that `a` is borrowed for `'static`

By making your Node object generic over the lifetime parameter 'a, you can lift that restriction. After that change, your Node<'a> objects will be bounded by the lifetime of .map(|d|...) reference

Resources:

Svetlin Zarev
  • 14,713
  • 4
  • 53
  • 82