When trying to work with closures that provide futures I am running into a number of types I have trouble expressing properly. How should the type T in the code below be properly specified in the generic code to allow the call from main below?
async fn foo<C, T>(f: C) -> bool
where
T: std::future::Future<Output=()>,
C: for <'a> FnOnce(&'a str) -> T
{
let bar = "str".to_string();
f(&bar).await;
true
}
#[tokio::main]
async fn main() {
let mut baz = "bla".to_string();
foo(|bar| async { baz.push_str(bar); }).await;
}
Essentially I would like to express the constraint that T should not live longer than 'a, but I haven't yet found a way to write that down legally. The "obvious" way of writing (T+'a) as the closure return type is not legal in rust 1.78. I have found the previous thread suggesting BoxFuture, but I would strongly prefer to avoid those as I want this to work in an environment without alloc.
See here and the following comment for an explanation. (Your case is a little simpler because there's no method receiver.) Or rephrased: T can never represent the future returned from your closure, because that future captures a lifetime -- it's parameterized by the input lifetime; it's not a single type.
There are a couple other hurdles to your OP:
Your closure also captuers a &mut to baz. Was that intentional?
If so, complicates things -- now there's another lifetime at play
Even if you don't need that, Rust's inference of closures you want to be higher-ranked is pretty bad
If there's a way around the first bullet point without boxing, I think it'd run into the second bullet point too -- and not have a workaround using async fn.
The capture of &mut baz was very intentional, it is quite critical to the use cases I want to support. I could deal with having to be more explicit on types if that is the solution for the second point. However, for the first I would really love to have some workaround, this feels like a quite strong missing factor in the type system. Is this a known issue of the type system?