What's the type of async functions?

I have a bit of code written in fully sync mode that I'd like to start transitioning to async/await. Here's the gist of what I'm starting with:

struct Logger;
impl Logger {
    fn log(&mut self, _msg: &str) {
        // ...
    }
}

struct Command {
    handler: Box<dyn Fn(&mut Logger) -> ()>,
    // ...
}

fn myhandler(logger: &mut Logger) {
    logger.log("myhandler logged this");
    // ...
}

fn main() {
    let mut mylogger = Logger {};
    let mycommand = Command { handler: Box::new(myhandler) };
    (mycommand.handler)(&mut mylogger);
}

(Playground)

That all works just fine. But now I'd really like to start writing async handler functions. It's not clear to me what the type of handler should be in Command then. My understanding is that async fn(A) -> B gets desugared into fn(A) -> Future<Output = B>, but I'm not able to turn this into compiling code no matter what I try.

Here's my latest, failed attempt.

So ultimately my question is: What is the type of async functions?

This might be good for a start;
References would require more work, so I converted them to Arc.

use std::future::Future;
use std::sync::Arc;

struct Logger;
impl Logger {
    fn log(&self, msg: &str) {
        println!("log: {}", msg);
    }
}

struct Command<R, T: Fn(Arc<Logger>) -> R> where R: Future<Output=()> {
    handler: T,
    // ...
}

async fn myhandler(logger: Arc<Logger>) {
    logger.log("myhandler logged this");
    // ...
}

#[tokio::main]
async fn main() {
    let mylogger = Arc::new(Logger {});
    let mycommand = Command { handler: &myhandler };
    (mycommand.handler)(mylogger.clone()).await;
}
1 Like

I recommend this thread about storing async function pointers in a vector.

3 Likes

Thank you @naim and @alice for the advice and pointers! I'm moving forward with something like this:

struct Command<R, H: Fn(Arc<Mutex<Logger>>) -> R> where R: Future<Output=()> {
    handler: H,
    // ...
}

I can sort of swallow the introduction of the type variable H since I can see how we may want some polymorphism over the handler function, but it's really quite annoying having to pass around R everywhere... I don't understand why I can't just do dyn Future<Output=()> like this.

On second thought, having to write


pub struct CLIApp<R, H: Fn(Arc<Mutex<UIHander>>) -> R>
where
  R: Future<Output = ()>,
{
  pub parts: Vec<CLIPart<R, H>>,
}

for every single struct and function that touches these objects is going to be a huge PITA. Is there any way out of this mess?

Ok new solution which avoids type variables but requires pinning and boxing the result: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7df1961643440d9c29b8cc310b37ae61

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.