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) 33 Answers
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; } - How do you pass a Rust function as a parameter?
- What is the concrete type of a future returned from `async fn`?
- What is the purpose of async/await in Rust?
- How can I store an async function in a struct and call it from a struct instance?
- What is the difference between `|_| async move {}` and `async move |_| {}`
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
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!() }