Is this the way to return somewhat closest to async closure?

use core::future::Future;
use std::pin::Pin;
use std::time::Duration;

type ClosureAsyncBlock = Box<dyn FnOnce(bool) -> Pin<Box<dyn Future<Output = ()>>>>;

fn returns_closure_async_block() -> ClosureAsyncBlock {
    Box::new(|cond| {
        Box::pin(async move {
            tokio::time::sleep(Duration::from_millis(1)).await;
            if cond {
                println!("Hello, world!");
            }
        })
    })
}

#[tokio::main]
async fn main() {
    let closure_async_block = returns_closure_async_block();
    closure_async_block(true).await;
}

(Playground)

Output:

Hello, world!

Errors:

   Compiling playground v0.0.1 (/playground)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.97s
     Running `target/debug/playground`

I'm making a function.
Based on outer value that is available after the function is finished, I might have to async operation or not.
In order to achieve this, I decided to return async closure but since it's not stable yet, I decided to return a closure that returns async block.

  1. If this approach is acceptable, did I get the type right?
  2. Is this approach acceptable, or is there a better way to do so?

The type system of the language is sound. This means that if the code you wrote passed type checking, then you got all the types right.

However, you usually want your futures to be Send.

Furthermore, the Pin<Box<dyn Future<…>>> type is verbose and therefore somewhat hard to read; consider using the BoxFuture type alias from the futures crate.

Yes.

4 Likes

Out of curiosity, why not just use async blocks where async closures would be needed?

short answer: "Based on outer value that is available after the function is finished, ..."

long answer:
I'm making a channel based session management system. When a session starts, it spawns message write task/message read task/system task. When a session is finished, it might start a different kind of session based on some runtime data.
Because the previous session's logic decides what new session gets constructed, I wanted to control the construction of new session inside the previous session. (In other words, return closure to control what happens after the system task is finished)
Spawning a new session is done by sending (writer, reader, session data) into tokio mpsc Sender.
Using just async block will not work, because the async block can't get writer and reader. (we can get writer and reader back when the message write/read task has been finished)

1 Like

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.