I have some async fn's that I'd like to add to a collection (which requires me to wrap them in a Box). I'd then like to call these functions with borrowed data that does not outlive the collection.
use futures::future::BoxFuture;
// some async fns I want to refer to as items of a Vec
async fn product(numbers: &[u64]) -> u64 { numbers.iter().product() }
async fn sum(numbers: &[u64]) -> u64 { numbers.iter().product() }
// a wrapper around the async fns so they have the same type
fn wrap<'a, H: 'static, F: 'a>(h: H) ->
Box<dyn Fn(&'a [u64]) -> BoxFuture<'a, u64>>
where
H: Fn(&'a [u64]) -> F,
F: std::future::Future<Output = u64> + Send,
{
Box::new(move |n| Box::pin(h(n)))
}
// THIS WORKS
// I can now refer to the async fns in a Vec
async fn example1() {
let numbers = vec![1, 2, 3];
let handlers = vec![wrap(product), wrap(sum)];
handlers[0](&numbers).await;
}
// THIS DOESN'T
// what if I want handlers to outlive numbers?
// what should the lifetimes be?
async fn example2() {
let handlers = vec![wrap(product), wrap(sum)];
{
let numbers = vec![1, 2, 3];
handlers[0](&numbers).await;
}
}
// THIS WORKS
// without the async fn wrapper it compiles
async fn example3() {
let handlers = vec![product];
{
let numbers = vec![1, 2, 3];
handlers[0](&numbers).await;
}
}
But this is not possible because then you can no longer talk about 'a on the type F. Unfortunately it is not possible to define a wrap function that does this due to limitations in the compiler. One option would be to define a macro:
macro_rules! wrap {
($closure:expr) => {{
#[allow(unused_mut)]
let mut closure = $closure;
let b: Box<dyn for<'a> Fn(&'a [u64]) -> BoxFuture<'a, u64>>
= Box::new(move |n| Box::pin(closure(n)));
b
}};
}
async fn example2() {
let handlers = vec![wrap!(product), wrap!(sum)];
{
let numbers = vec![1, 2, 3];
handlers[0](&numbers).await;
}
}
This sidesteps the issues of what you can say in a function signature by just not having any function signature.
Thanks, I spent a long time trying to get that lifetime into the right place in the signature and just assumed it must be possible. Using a macro isn't ideal, but the code now compiles at least!
Perhaps not as a serious suggestion, but I actually managed to define a wrap function, using a lot of nightly unstable features, that does makes this compile: