Reputation: 1989
I'm having some problems trying to understand how Rust lifetimes works in some situations, like below. I can't get it working but I'm not sure why.
struct Bar {
value: &'static str,
}
struct Foo<'a, T: 'a> {
bar: &'a T,
}
fn main() {
let mut foos = Vec::new();
let y = Bar {
value: "Hello, world!",
};
let x = Foo { bar: &y };
foos.push(x);
}
error[E0597]: `y` does not live long enough
--> src/main.rs:15:25
|
15 | let x = Foo { bar: &y };
| ^ borrowed value does not live long enough
...
18 | }
| - `y` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
This is a simplified example of what I'm actually trying to achieve:
fn main() {
let foos = vec![
Foo { bar: &(Bar { value: "a" }) },
Foo { bar: &(Bar { value: "b" }) },
];
}
I'd appreciate any ideas, thoughts or explanations.
Upvotes: 4
Views: 1082
Reputation: 431599
Both versions of your code now work, thanks to non-lexical lifetimes.
Your problem can be simplified to this example:
fn main() {
let mut foos = Vec::new();
let y = &42;
foos.push(y);
}
The important thing to remember in this case is that variables are destroyed in the inverse order they are created. You could almost look at the code as
fn main() {
let mut foos = Vec::new();
{
let y = &42;
{
foos.push(y);
}
// destroy y
}}
// destroy foos
}
For simple values like I am showing, this doesn't really matter, but it's more important when you have complicated types that have custom Drop
implementations.
A simple fix is to reorder the statements:
fn main() {
let y = &42;
let mut foos = Vec::new();
foos.push(y);
}
Now, it's guaranteed that the thing being referred to lives longer than the reference stored in the vector. For your original reduced example, this works:
let y = Bar { value: "Hello, world!" };
let x = Foo { bar: &y };
let mut foos = Vec::new();
foos.push(x);
Your original code has a bit trickier problem. Let's look at the expansion of the vec!
macro:
let foos = <[_]>::into_vec(Box::new([Foo { bar: &(Bar { value: "a" }) }]));
Which we can simplify down to
let foos = Box::new(&42);
The problem is that the temporary variable is, well, temporary. It only exists for the duration of the function call. This means that the reference to the temporary variable can't last longer than that. That's why the error message suggests "consider using a let
binding to increase its lifetime". By doing so, the variable will live longer than the function call.
Is it possible to make the temporary value last longer without using a let statement? The vector will have many values, say 30. So I'll have to place 30 let statements?
No, you have to be explicit about how long they should live, so you need to be explicit about where they are. I see two solutions:
Change your structures so that they own items, instead of referring to them:
struct Foo<T> {
bar: T,
}
let foos = vec![
Foo { bar: Bar { value: "a" } },
Foo { bar: Bar { value: "b" } },
];
Create a vector that owns all the inner types, then map over it to get references:
let bars = vec![Bar { value: "a" }, Bar { value: "b" }];
let foos: Vec<_> = bars.iter().map(|bar| Foo { bar: bar }).collect();
Upvotes: 1