I'd like to make the following code compile:
struct Provider {}
impl Provider {
fn get_string<'a>(&'a self) -> &'a str { "this is a string" }
}
fn main() {
let provider = Provider{};
let mut vec: Vec<&str> = Vec::new();
// PROBLEM: how do I say that this reference s here
// needs to live as long as vec?
let fun = |s: &str| {
vec.push(s);
};
fun(provider.get_string());
}
Playground link
This is the compile error that I get:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:9:22
|
9 | let mut vec: Vec<&str> = Vec::new();
| ^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the block at 11:24...
--> src/main.rs:11:25
|
11| let fun = |s: &str| {
| ^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:12:18
|
12| vec.push(s);
| ^
note: but, the lifetime must be valid for the block suffix following statement 2 at 13:6...
--> src/main.rs:13:7
|
13| };
| ^
note: ...so that variable is valid at time of its declaration
--> src/main.rs:11:9
|
11| let fun = |s: &str| {
| ^^^
Your code works just fine if remove all the lifetime annotations and let the compiler inference do its job:
struct Provider;
impl Provider {
fn get_string(&self) -> &str { "this is a string" }
}
fn main() {
let provider = Provider;
let mut vec = Vec::new();
let mut fun = |s| {
vec.push(s);
};
fun(provider.get_string());
}
In short, there's no way to explicitly refer to the lifetime of a local variable, only function arguments. The compiler knows how to do it, though.
If you truly needed it, you could create a function to allow annotating the lifetimes:
fn thing<'a>(provider: &'a Provider) -> Vec<&'a str> {
let mut vec: Vec<&'a str> = Vec::new();
{
let mut fun = |s: &'a str| vec.push(s);
fun(provider.get_string());
} // End mutable borrow of `vec`
vec
}
fn main() {
let provider = Provider;
thing(&provider);
}
why did the original annotations stop things from working?
Specifically, it's this bit:
let fun = |s: &str| {
vec.push(s);
};
This declares a new lifetime on the closure. Using a made-up syntax (you can't declare lifetimes on closure arguments), it would be equivalent to:
let fun = <'a> |s: &'a str| {
vec.push(s);
};
Which is why the compiler has the error:
the lifetime cannot outlive the anonymous lifetime #1 defined on [the closure's block]
There's no connection between that generated lifetime and that of the Provider
. Leaving it out allows the compiler to insert the desired but unnamable lifetime.
Here's a version which compiles:
use std::marker::PhantomData;
struct Provider<'a> {
_dummy: PhantomData<&'a ()>,
}
impl<'a> Provider<'a> {
fn get_string(&self) -> &'a str {
"this is a string"
}
}
fn f<'b>() {
let provider = Provider { _dummy: PhantomData };
let mut vec: Vec<&str> = Vec::new();
// PROBLEM: how do I say that this reference s here
// needs to live as long as vec?
let mut fun = |s: &'b str| { vec.push(s); };
fun(provider.get_string());
}
fn main() {
f()
}
Playground link
I made the following changes:
- Add a lifetime to
Provider
(I added a PhantomData
, but I guess your provider already owns some data it'll provide).
- Update the
get_string
method to show that it returns something with the provider's lifetime, not the input lifetime (ie based on the Provider
's lifetime parameter).
- Add a new lifetime parameter
'b
to the function (which I renamed to f()
, since main()
can't have one), which I use to name the lifetime of the closure parameter.
The last one is slightly confusing, as apparently merely adding a name to a lifetime (without apparently adding any constraints) has made it work.
I think (but I'd love some documentation for this) that this is because of lifetime elision. A closure is really a hidden struct
with a fn call(&self, s: &str)
(in this case) method. According to the lifetime elision rules, the s
parameter gains the same lifetime as &self
, which is the closure itself. In this case, the closure is declared after vec
, so the lifetime is too short. The explicit lifetime means that it is decoupled from the closure's own lifetime.