33

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)
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Skarlett
  • 762
  • 1
  • 10
  • 22
  • 1
    I think you need a Stream to achieve that, have a look at the crate futures – Mario Santini Mar 17 '20 at 07:18
  • @MarioSantini https://docs.rs/async-std/1.5.0/async_std/stream/trait.Stream.html#method.any It does not appear obvious that the functions they're accepting are async, rather the final result appears to be. – Skarlett Mar 17 '20 at 07:26
  • 1
    What I ment, was just: if you need to iterate asynchrounously on a collection, you may need a stream instead, that is a feature on which you can iterate. – Mario Santini Mar 17 '20 at 08:51

2 Answers2

55

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;
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • I Dart if the `.await` is left off it results in `-> Future>` it is flattened automatically to `Future`, so the `.await` is implicit. I haven't found anything so far if Rust does this as well. – Günter Zöchbauer Apr 07 '23 at 15:17
  • 1
    @GünterZöchbauer not that I'm aware of. Such a thing being implicit feels a little too "loose" to me for Rust, so I'd be surprised if it were added. – Shepmaster Apr 14 '23 at 20:37
8

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

attdona
  • 17,196
  • 7
  • 49
  • 60