Reputation: 942
I would like to replicate the behavior and ergonomics of taking a closure/function as an argument much like map
does: iterator.map(|x| ...)
.
I've noticed that some library code allows passing in async functionality, but this method doesn't allow me to pass in arguments:
pub fn spawn<F, T>(future: F) -> JoinHandle<T>
where
F: Future<Output = T> + Send + 'static,
T: Send + 'static,
spawn(async { foo().await });
I'm hoping to do one of the following:
iterator.map(async |x| {...});
async fn a(x: _) {}
iterator.map(a)
Upvotes: 47
Views: 28743
Reputation: 27357
You can use
the trait bound for async closures
async Fn[Once/Mut](Args) -> ReturnType
(although this may be changed):#![feature(async_closure)] async fn my_func<F>(closure: F) where F: async Fn(), { closure().await; closure().await; closure().await; }
This translates into a bound on
AsyncFn[Once/Mut]
, but you can't spell these traits directly.
As explained by Chayim in this duplicate
Upvotes: 0
Reputation: 11
It's too late but I hope this code helps others. In my situation I have some predefined functions which I can not change them (func0, func1, ...) and I need to run them all till the last one in order. If one fails, the whole operation must terminate. So I created an array of functions, then each item will get executed during a loop.
use std::future::Future;
use std::pin::Pin;
type FuncResult = dyn Future<Output=Result<(), String>>;
async fn apply_funcs(mut start: usize, end: usize) {
let funcs = [func0];
let mut result = true;
while start < end && result {
result = func_wrapper(funcs[start]).await;
start += 1;
}
}
async fn func_wrapper(f: impl FnOnce() -> FuncResult) -> bool {
match f().await {
Ok(_) => { println!("ok"); }
Err(e) => {
println!("{e}");
return false;
}
}
true
}
async fn func0() -> Result<(), String> {
todo!()
}
This code fails: expected dyn Future
, found future
I found 2 ways to solve that:
1: By static dispatching which is by generics:
async fn apply_funcs(mut start: usize, end: usize) {
let funcs = [func0];
let mut result = true;
while start < end && result {
result = func_wrapper(funcs[start]).await;
start += 1;
}
}
async fn func_wrapper<Fut>(f: impl FnOnce() -> Fut) -> bool
where Fut: Future<Output=Result<(), String>> {
match f().await {
Ok(_) => { println!("ok"); }
Err(e) => {
println!("{e}");
return false;
}
}
true
}
async fn func0() -> Result<(), String> {
todo!()
}
2: By dynamic dispatching via defining another closure:
type FuncResult = Pin<Box<dyn Future<Output=Result<(), String>>>>;
async fn apply_funcs(mut start: usize, end: usize) {
let funcs = [
|| -> FuncResult {
Box::pin(func0())// no await here
}
];
let mut result = true;
while start < end && result {
result = func_wrapper(funcs[start]).await;
start += 1;
}
}
async fn func_wrapper(f: impl FnOnce() -> FuncResult) -> bool {
match f().await {
Ok(_) => { println!("ok"); }
Err(e) => {
println!("{e}");
return false;
}
}
true
}
async fn func0() -> Result<(), String> {
todo!()
}
Upvotes: 1
Reputation: 430711
async
functions are effectively desugared as returning impl Future
. Once you know that, it's a matter of combining existing Rust techniques to accept a function / closure, resulting in a function with two generic types:
use std::future::Future;
async fn example<F, Fut>(f: F)
where
F: FnOnce(i32, i32) -> Fut,
Fut: Future<Output = bool>,
{
f(1, 2).await;
}
This can also be written as
use std::future::Future;
async fn example<Fut>(f: impl FnOnce(i32, i32) -> Fut)
where
Fut: Future<Output = bool>,
{
f(1, 2).await;
}
Upvotes: 75
Reputation: 18943
The async |...| expr
closure syntax is available on the nightly channel enabling the feature async_closure
.
#![feature(async_closure)]
use futures::future;
use futures::Future;
use tokio;
pub struct Bar;
impl Bar {
pub fn map<F, T>(&self, f: F)
where
F: Fn(i32) -> T,
T: Future<Output = Result<i32, i32>> + Send + 'static,
{
tokio::spawn(f(1));
}
}
async fn foo(x: i32) -> Result<i32, i32> {
println!("running foo");
future::ok::<i32, i32>(x).await
}
#[tokio::main]
async fn main() {
let bar = Bar;
let x = 1;
bar.map(foo);
bar.map(async move |x| {
println!("hello from async closure.");
future::ok::<i32, i32>(x).await
});
}
See the 2394-async_await RFC for more detalis
Upvotes: 9