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:
Provider
(I added a PhantomData
, but I guess your provider already owns some data it'll provide).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).'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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With