I was making the executor/reactor while discovered this a lifetime problem. It is not related to async/Future and can be reproduced without async sugar.
use std::future::Future;
struct Runtime;
fn start_with_runtime<C, F>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> F,
F: Future
{
let rt = Runtime;
let _future = closure(&rt);
// block_on(future);
}
async fn async_main(_rt: &Runtime) {
// I can use _rt to do async stuff here
}
fn main() {
start_with_runtime(|rt| { async_main(rt) });
}
I would like to the start_with_runtime() to run the future and provide the async Runtime reference as parameter.
It does not compile:
error: lifetime may not live long enough
--> src/main.rs:17:31
|
17 | start_with_runtime(|rt| { async_main(rt) });
| --- ^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is impl std::future::Future
| has type `&'1 Runtime`
I think that this problem seems to be because how rust infers lifetimes for closures:
https://github.com/rust-lang/rust/issues/58052 :
fn main() {
let f = |x: &i32| x;
let i = &3;
let j = f(i);
}
Does not compiles either:
error: lifetime may not live long enough
--> src/main.rs:2:23
|
2 | let f = |x: &i32| x;
| - - ^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is &'2 i32
| let's call the lifetime of this reference `'1`
Looks like my closure signature inferred as |&'a Runtime| -> impl Future + 'b and thus the lifetime error. I feel that given correct expected signature for closure would help, but how do I provide the correct signature in start_with_runtime?
fn start_with_runtime<C>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> (impl Future + 'a),
Does not work because impl Trait is not allowed here.
fn start_with_runtime<C,F>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> F,
F: Future + 'a
Does not work as well because 'a is not known outside of HRTB expression.
It works if I know the type:
struct MyType<'a> {
_rt: &'a Runtime
}
fn start_with_runtime<C>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> MyType<'a>,
This is kind of sad when you've thought through all the lifetimes but language does not provide a way to express this. Perhaps there is a trick in rust to make this work?
There seem to be two different questions in this one: can the required relationship be expressed in Rust syntax and will it work with closure type inference or not.
Let's start with the first one. You're right that this cannot be expressed with just where clauses. To express this one needs to add a helper trait
trait BorrowingFn<'a> {
type Fut: std::future::Future<Output = Something> + 'a;
fn call(self, arg: &'a Runtime) -> Self::Fut;
}
that allows the bound we need to be written as
C: for<'a> BorrowingFn<'a>,
and provide a blanket implementation of this trait for all applicable functions
impl<'a, Fu: 'a, F> BorrowingFn<'a> for F
where
F: FnOnce(&'a Runtime) -> Fu,
Fu: std::future::Future<Output = ()> + 'a,
{
type Fut = Fu;
fn call(self, rt: &'a Runtime) -> Fu {
self(rt)
}
}
(playground)
Ok, so it works with an async function, but does it work with a closure that needs type inference? Unfortunately, the answer is "no"
error: implementation of `BorrowingFn` is not general enough
--> src/main.rs:33:5
|
5 | / trait BorrowingFn<'a> {
6 | | type Fut: std::future::Future<Output = ()> + 'a;
7 | | fn call(self, arg: &'a Runtime) -> Self::Fut;
8 | | }
| |_- trait `BorrowingFn` defined here
...
33 | start_with_runtime(|rt| async_main(rt)); // however, it does not work with closure type inference :-(
| ^^^^^^^^^^^^^^^^^^ implementation of `BorrowingFn` is not general enough
|
= note: `[closure#src/main.rs:33:24: 33:43]` must implement `BorrowingFn<'0>`, for any lifetime `'0`...
= note: ...but `[closure#src/main.rs:33:24: 33:43]` actually implements `BorrowingFn<'1>`, for some specific lifetime `'1`
This is being tracked in rust-lang/rust#70263. The compiler is not smart enough yet to notice that this closure needs a higher-rank type.
For fun I tried compiling with -Z chalk on Nightly, but it's not yet ready for this (internal compiler error).
Sorry, this is a limitation in the language. You can only specify the lifetime on concrete types. One workaround is to use the trait object type.
fn start_with_runtime<C, F, T>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> Pin<Box<dyn Future<Item = T> + Send + 'a>>,
{
let rt = Runtime;
let _future = closure(&rt);
// block_on(future);
}
Related
Compilation of this snippet:
trait Base {
type T;
fn get_p(&self) -> &Self::T;
}
trait OnBase: Base {
fn get_a(&self) -> &A;
}
impl<S, T> OnBase for S
where
S: Base<T = dyn OnBase<T = T>>,
{
fn get_a(&self) -> &A {
self.get_p().get_a()
}
}
struct A {}
Fails with:
error[E0311]: the parameter type `T` may not live long enough
--> src/blanket_with_ref.rs:17:9
|
17 | self.get_p().get_a()
| ^^^^^^^^^^^^
|
note: the parameter type `T` must be valid for the anonymous lifetime defined here...
--> src/blanket_with_ref.rs:16:14
|
16 | fn get_a(&self) -> &A {
| ^^^^^
note: ...so that the type `T` will meet its required lifetime bounds
--> src/blanket_with_ref.rs:17:9
|
17 | self.get_p().get_a()
| ^^^^^^^^^^^^
help: consider adding an explicit lifetime bound...
|
14 | impl <S, T: 'a> OnBase for S where S:Base<T=dyn OnBase<T=T>> {
| ++++
I vaguely comprehend that I must somehow tell it that lifetimes of Base and OnBase should be same but even if I add 'a to all traits and refrences it keeps failing.
Is it possible to somehow make it compile?
P.S. - it works if get_a returns plain A.
pps - in the real app it should be a kind of a strategy delegating to whatever impl it encapsulates
playground
This is probably what you actually want, instead of T = dyn OnBase<T = T>:
trait Base {
type T;
fn get_p(&self) -> &Self::T;
}
trait OnBase: Base {
fn get_a(&self) -> &A;
}
impl<S> OnBase for S
where
S: Base,
<S as Base>::T: OnBase,
{
fn get_a(&self) -> &A {
self.get_p().get_a()
}
}
struct A;
I'm not sure what purpose OnBase: Base serves, though. If OnBase is already Base, then why do any of this? And what should it return in get_p? With the current layout, it's really easy to get caught up in an infinite get_a recursion.
In trying to compile the code
fn make<DomainType>(foo: fn(DomainType) -> ()) -> Box<dyn Fn(DomainType) -> ()> {
let foo_clone = foo.clone();
let bar = move |input: DomainType| {foo_clone(input)};
Box::new(bar)
}
fn main() {}
The compiler gives
error[E0310]: the parameter type `DomainType` may not live long enough
--> src/main.rs:4:5
|
4 | Box::new(bar)
| ^^^^^^^^^^^^^ ...so that the type `[closure#src/main.rs:3:15: 3:58]` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound...
|
1 | fn make<DomainType: 'static>(foo: fn(DomainType) -> ()) -> Box<dyn Fn(DomainType) -> ()> {
| +++++++++
For more information about this error, try `rustc --explain E0310`.
I have been reading through similar questions
Parameter type may not live long enough?
and
The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want
But in both of those examples, the boxed trait is what needs the added lifetime. In this case it's apparently just the DomainType of the closure trait. I'm quite baffled what to make of this.
My understanding of lifetime variables in function definitions is that they connect the lifetime of the returned type (which I can understand is implicitly static here, or at least the thing inside the box is) to those (perhaps elided) lifetimes of the function parameters. But no function parameter here is a DomainType.
DISCLAIMER: This question goes very deep into Rust compiler specifics, and I'm not 100% sure about everything I say here. Take everything with a grain of salt.
The problem here is that Box<T> means that T is 'static if no lifetime is annotated.
A generic, however, does not mean that. A generic could contain any kind of lifetime. To match the two, you need an explicit lifetime annotation.
Solution 1
You could annotate 'static to the generic, like this:
fn make<DomainType: 'static>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType)> {
Box::new(foo)
}
That, however, would create problems if you do actually encounter the case where the generic carries a lifetime:
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn main() {
let x = "abc".to_string();
let f = make(my_func);
f(&x);
}
error[E0597]: `x` does not live long enough
--> src/main.rs:13:7
|
13 | f(&x);
| --^^-
| | |
| | borrowed value does not live long enough
| argument requires that `x` is borrowed for `'static`
14 | }
| - `x` dropped here while still borrowed
Solution 2
The proper way here is to solve the initial mismatch by telling the compiler that the lifetimes attached to the generic match the lifetimes contained in the Box.
Like this:
fn make<'a, DomainType: 'a>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType) + 'a> {
Box::new(foo)
}
And it now works:
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn main() {
let x = "abc".to_string();
let f = make(my_func);
f(&x);
}
my_func: abc
Problems
There are still problems with this. I'm unsure how/if they are solvable, though.
The problem is that the resulting Box object will always be bound to a specific lifetime. Look for example at this:
fn make<'a, DomainType: 'a>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType) + 'a> {
Box::new(foo)
}
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn make2() -> Box<dyn Fn(&str)> {
Box::new(|x| println!("closure: {}", x))
}
fn main() {
let x = "abc".to_string();
let f = make(my_func);
let f2 = make2();
let fs = [f2, f];
}
error[E0308]: mismatched types
--> src/main.rs:19:19
|
19 | let fs = [f2, f];
| ^ one type is more general than the other
|
= note: expected trait object `dyn for<'r> Fn(&'r str)`
found trait object `dyn Fn(&str)`
f2 here takes all objects that can be borrowed. f only takes objects that all can be borrowed with the same lifetime, as 'a resolves when make is called, not when f is executed.
Another example is this:
fn make<'a, DomainType: 'a>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType) + 'a> {
Box::new(foo)
}
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn make2() -> Box<dyn Fn(&str)> {
Box::new(|x| println!("closure: {}", x))
}
fn main() {
let f = make(my_func);
let f2 = make2();
{
let x = "abc".to_string();
f2(&x); // Works
f(&x); // Fails to compile
}
}
error[E0597]: `x` does not live long enough
--> src/main.rs:20:11
|
20 | f(&x); // Fails to compile
| ^^ borrowed value does not live long enough
21 | }
| - `x` dropped here while still borrowed
22 | }
| - borrow might be used here, when `f` is dropped and runs the destructor for type `Box<dyn Fn(&str)>`
|
= note: values in a scope are dropped in the opposite order they are defined
I don't think this is solvable, though. I think generics can't carry generic lifetimes, they can only carry specific lifetimes. And said specific lifetime gets resolved at the point where make() is called.
Although a proper explanation would require someone with a deeper Rust compiler understanding than me. If something like this exists, I'd be interested in it as well. (something like DomainType: for<'a> or similar)
I can see that one of the example's output says it would require a dyn for<'r> Fn(&'r str). I don't know, however, if something like this is annotatable by hand or if it's something internal to the compiler. But that would be what is required - instead of Box<dyn Fn(DomainType) + 'a>, we would need something like Box<dyn for <'r> Fn(DomainType + 'r)>. Again, I don't know if/how this is annotatable, and if it is, I'd also be interested in it.
Could Higher Rank Trait Bounds be what you're looking for?
fn make<F, DomainType>(foo: F) -> Box<dyn Fn(DomainType)>
where
for<'r> F: Fn(DomainType) + Clone + 'r
{
let foo_clone = foo.clone();
let bar = move |input: DomainType| {foo_clone(input)};
Box::new(bar)
}
If the purpose of the function is to consume a closure and return it in a Box, I don't think you need the Clone trait. In fact, it doesn't even need to be nested in a new closure. Just directly box it.
fn make<F, DomainType>(foo: F) -> Box<dyn Fn(DomainType)>
where
for<'r> F: Fn(DomainType) + 'r
{
Box::new(foo)
}
Unless you want to pass in a reference to a closure that doesn't get consumed:
fn make<F, DomainType>(foo: &F) -> Box<dyn Fn(DomainType)>
where
for<'r> F: Fn(DomainType) + Clone + 'r
{
Box::new(foo.clone())
}
I have async function to which I am passing async callback. The callback takes a reference as a parameter.
use core::future::Future;
async fn foo(bar: &u32) {}
async fn baz<F, Fut>(f: F)
where
F: FnOnce(&u32) -> Fut,
Fut: Future<Output = ()>,
{
let test: u32 = 42;
f(&test).await;
}
#[tokio::main]
async fn main() {
baz(foo).await;
}
I am getting the following error if I try to build this (playground):
error[E0308]: mismatched types
--> src/main.rs:16:5
|
16 | baz(foo).await;
| ^^^ lifetime mismatch
|
= note: expected associated type `<for<'_> fn(&u32) -> impl Future<Output = ()> {foo} as FnOnce<(&u32,)>>::Output`
found associated type `<for<'_> fn(&u32) -> impl Future<Output = ()> {foo} as FnOnce<(&u32,)>>::Output`
= note: the required lifetime does not necessarily outlive the empty lifetime
note: the lifetime requirement is introduced here
--> src/main.rs:7:24
|
7 | F: FnOnce(&u32) -> Fut,
| ^^^
I understand that it's not happy about the lifetime of the reference. However, I don't understand why.
We borrow "test"
We execute callback f (which is "foo")
There is no way for baz exits before f is done
So, it looks like there is no way for the borrow to outlive the place where test is declared.
What am I missing?
The future returned by foo() has a hidden lifetime. The desugared signature is like:
fn foo<'a>(bar: &'a u32) -> impl Future<Output = ()> + 'a {
async move {}
}
This is done so the function can hold bar across .await points. Sadly, what it means is that the function does not fulfill baz()'s bounds. The errors are obfuscated because the lifetime is hidden, but this is what the compiler is trying to tell you: the bound should be something like where F: for<'a> FnOnce(&'a u32) -> impl Future<Output = ()> + 'a, but you cannot express that in current Rust.
For more and potential solutions, see for example:
Calling a generic async function with a (mutably) borrowed argument
Specify Rust closures lifetime
Static future doesn't live long enough
Generic for FnOnce that returns a future with a lifetime
Just for solving, you can use Box instead of references.
I'm trying to write a method on a struct that returns a closure. This closure should take a &[u8] with an arbitrary lifetime 'inner as an argument and return the same type, &'inner [u8]. To perform its function, the closure also needs a (shared) reference to a member of the struct &self. Here is my code:
#![warn(clippy::pedantic)]
// placeholder for a large type that I can't afford to clone
struct Opaque(usize);
struct MyStruct<'a>(Vec<&'a Opaque>);
impl<'a> MyStruct<'a> {
pub fn get_func_for_index(
&'a self,
n: usize,
) -> Option<impl for<'inner> Fn(&'inner [u8]) -> &'inner [u8] + 'a> {
// the reference to the struct member, captured by the closure
let opaque: &'a Opaque = *self.0.get(n)?;
Some(move |i: &[u8]| {
// placeholder: do something with the input using the &Opaque
&i[opaque.0..]
})
}
}
fn main() {
let o1 = Opaque(1);
let o5 = Opaque(5);
let o7 = Opaque(7);
let x = MyStruct(vec![&o1, &o5, &o7]);
let drop_five = x.get_func_for_index(1 /*Opaque(5)*/).unwrap();
let data: Vec<u8> = Vec::from(&b"testing"[..]);
assert_eq!(drop_five(&data[..]), b"ng");
}
If I try to compile it with rustc 1.54.0 (a178d0322 2021-07-26), I get the following error:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
--> /tmp/lifetimes.rs:16:14
|
16 | &i[opaque.0..]
| ^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 14:14...
--> /tmp/lifetimes.rs:14:14
|
14 | Some(move |i: &[u8]| {
| ______________^
15 | | // placeholder: do something with the input using the &Opaque
16 | | &i[opaque.0..]
17 | | })
| |_________^
note: ...so that reference does not outlive borrowed content
--> /tmp/lifetimes.rs:16:14
|
16 | &i[opaque.0..]
| ^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 6:6...
--> /tmp/lifetimes.rs:6:6
|
6 | impl<'a> MyStruct<'a> {
| ^^
note: ...so that return value is valid for the call
--> /tmp/lifetimes.rs:10:17
|
10 | ) -> Option<impl for<'inner> Fn(&'inner [u8]) -> &'inner [u8] + 'a> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: higher-ranked subtype error
--> /tmp/lifetimes.rs:7:5
|
7 | / pub fn get_func_for_index(
8 | | &'a self,
9 | | n: usize,
10 | | ) -> Option<impl for<'inner> Fn(&'inner [u8]) -> &'inner [u8] + 'a> {
| |_______________________________________________________________________^
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0495`.
It's quite a mouthful and I don't really understand what it's trying to tell me. The first part (first, the lifetime...) makes sense to me, the returned slice must not outlive the closure argument. The second part (but, the lifetime...) however seems weird to me - the + 'a annotation in the method signature refers to the closure itself (because it captures &'a self.foo), not to the value the closure returns.
Is it possible to change the code to model this correctly in rust, or is this construct just not supported at this time?
The problem is here:
Some(move |i: &[u8]| {
Every & has a lifetime on it, explicit or not. What is the lifetime of &[u8]? Clearly it should be "a lifetime chosen by the caller of the closure" (that is, a higher-ranked lifetime). But when the compiler encounters a reference type with a free lifetime parameter, even in the argument list of a closure, it will not assume that the lifetime is higher-ranked. The error message you get about the "anonymous lifetime #1" is the compiler confusedly trying to make a non-higher-ranked lifetime work.
The compiler could, in theory, work "backwards" from the impl Fn in the return type and recognize that the type of the closure needs to have this higher ranked lifetime. It's not quite clever enough to do that in this case, but there is a way to convince it: use a local function with a bounded type parameter to constrain the type of the closure to exactly the bound you want.
pub fn get_func_for_index(
&self, // note 1
n: usize,
) -> Option<impl 'a + for<'inner> Fn(&'inner [u8]) -> &'inner [u8]> { // note 2
// the reference to the struct member, captured by the closure
let opaque: &'a Opaque = *self.0.get(n)?;
// helper function to constrain the closure type
fn check<F: Fn(&[u8]) -> &[u8]>(f: F) -> F { // note 3
f
}
Some(check(move |i| {
// placeholder: do something with the input using the &Opaque
&i[opaque.0..]
}))
}
Playground
Please note the following:
&'a self is too conservative for this function because 'a is the lifetime parameter of the reference self contains, not the lifetime for which self is borrowed. In general, you should almost never write &'a self or &'a mut self where 'a is a named lifetime from an outer scope.
I find the + 'a easy to miss at the end of a long trait, especially a Fn trait with a return type. I recommend fronting the lifetime (putting it first) in cases like this to make clear that it relates more to the impl than to the &'inner [u8]. This is a stylistic choice.
Fn(&[u8]) -> &[u8] is actually exactly the same as for<'inner> Fn(&'inner [u8]) -> &'inner [u8], because the elision rules for Fn traits are the same as for regular functions. Either way is fine; I find the shorter version easier to read.
Similar questions
Expected bound lifetime parameter, found concrete lifetime [E0271]
How to declare a higher-ranked lifetime for a closure argument?
Why does this closure require inlining or `dyn`? What does `dyn` do here?
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:
The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want
How can this instance seemingly outlive its own parameter lifetime?
How can we write a generic function for checking Serde serialization and deserialization?