I wrote the code below, but I can't write life time constraint to work and get an error:
use futures::Future;
async fn foo<'a>(a: &'a str) -> &'a str {
let task = get();
f(a, task).await
}
async fn f<T>(v: T, task: impl Future<Output = T>) -> T {
if true {
v
} else {
task.await
}
}
async fn get() -> &'static str {
"foo"
}
error:
error[E0759]: `a` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement
--> src/lib.rs:3:18
|
3 | async fn foo<'a>(a: &'a str) -> &'a str {
| ^ ------- this data with lifetime `'a`...
| |
| ...is captured here...
4 | let task = get();
5 | f(a, task).await
| - ...and is required to live as long as `'static` here
playground
I think it can be solved if two parameters in function f
can have their own lifetimes.
For example,
v: T,
task: S,
T: 'a,
S: 'b,
'b: 'a,
S == T
How to solve this issue?
The same problem can be reproduced with another minimal example, using function interfaces instead of async functions.
fn get() -> impl FnOnce() -> &'static str {
|| "foo"
}
fn foo<'a, T: 'a, F>(_: &'a str, _: F)
where
F: Fn() -> T,
T: FnOnce() -> &'a str,
{
}
let x = "".to_string();
foo(&*x, &get);
error[E0597]: `x` does not live long enough
--> src/main.rs:22:11
|
22 | foo(&*x, &get);
| ------^-------
| | |
| | borrowed value does not live long enough
| argument requires that `x` is borrowed for `'static`
23 | }
| - `x` dropped here while still borrowed
This example allows us to turn get
into a function parameter and observe that passing this function imposes a hard constraint for the lifetime 'a
to be 'static
. Despite the program's best intentions, a function returning a supplier function (or a promise) does not provide co-variance with respect to the output's lifetime. That is, () -> &'static str
does not fulfill for<'a> () -> &'a str
. Occasionally, the compiler will fallback to suggesting you to stick to the weakest link that is the 'static
lifetime, even though this may not be desirable.
Note that the means of representing types which are generic over their lifetimes is quite limited at the moment. These are a form of higher kinded types, which can be specified only to some level of expressiveness via higher ranked trait bounds (and eventually generic associated types, once they are fully implemented and stabilized). In this case, rather than trying to make f
work for a kind T<'a>
(pseudo-code), it is much better to just make our get
generic over the lifetime 'a
. Subtyping may then take place at the implementation, as we know that a string literal can fulfill any lifetime.
fn get<'a>() -> impl FnOnce() -> &'a str {
|| "foo"
}
In the async
case (Playground):
async fn get<'a>() -> &'a str {
"foo"
}
See also:
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