How to return an async closure from a function

Hi there. I try to use async closures but I am failing.

The non-async is easy-peasy:

fn add(x: i32, y: i32) -> i32 {
    x + y
}

fn make_adder(x: i32) -> impl Fn(i32) -> i32 {
    move |y: i32| add(x, y)
}

fn main() {
    let adder = make_adder(2);
    dbg!(adder(40));
}

But I don't get the async case working:

async fn async_add(x: i32, y: i32) -> i32 {
    x + y
}

fn make_async_adder(x: i32) -> ?????? {
    move |y: i32| ??????
}

#[tokio::main]
async fn main() {
    let async_adder = make_async_adder(2);
    dbg!(async_adder(40).await);
}

I found a lot about Box, Pin, Future and dyn, but simply don't get it.

It would be super nice if someone could tell me what needs to be filled into the two places marked with "??????".

Thank you very much in advance!

You just need Box::pin and a trait object.

You could make the closure a trait object as well, but you don't need to in this case.

Playground

use std::{future::Future, pin::Pin};

async fn async_add(x: i32, y: i32) -> i32 {
    x + y
}

fn make_async_adder(x: i32) -> impl Fn(i32) -> Pin<Box<dyn Future<Output = i32>>> {
    move |y: i32| Box::pin(async_add(x, y))
}

#[tokio::main]
async fn main() {
    let async_adder = make_async_adder(2);
    dbg!(async_adder(40).await);
}
2 Likes

In theory, you could double-impl this, like:

fn make_async_adder(x: i32) -> impl Fn(i32) -> impl Future<Output = i32> {
    move |y: i32| async_add(x, y)
}

That's currently an error:

error[E0562]: `impl Trait` only allowed in function and inherent method return types, not in `Fn` trait return
 --> src/main.rs:7:48
  |
7 | fn make_async_adder(x: i32) -> impl Fn(i32) -> impl Future<Output = i32> {
  |                                                ^^^^^^^^^^^^^^^^^^^^^^^^^

But with nightly #![feature(unboxed_closures)], it does work spelled out as the raw Fn trait:

fn make_async_adder(x: i32) -> impl Fn<(i32,), Output = impl Future<Output = i32>> {
    move |y: i32| async_add(x, y)
}
2 Likes

An async function is really just a regular function that returns a Future. Because Future is a trait, what that really means is that it returns some type that implements Future.

Every async function returns a different "custom made" type specific to that function and there's no way to name it, but we can use the impl Trait syntax to say "some type that implements this trait". Another way to write async_add is

// returns something that implements a Future that resolves to an i32
fn async_add(x: i32, y: i32) -> impl Future<Output = i32> {
    async move {
        x + y
    }
}

Which is what you see in cuviper's code. Unfortunately, as you can see from the error, you can't just write impl Fn(i32) -> impl Future<Output = i32> (at least not yet)... So you need either the Pin<Box<...>> from semicoleon's post or nightly features like in cuviper's post.

If you want to learn more about async, you can check out Getting Started - Asynchronous Programming in Rust which goes over this stuff in much more detail.

3 Likes

Thanks to all of you for explanations. With these examples working I can explore and study the subject further.

Thx!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.