Pass an async function as parameter

First I defined a callback function

type CallbackFunction =
    Box<dyn FnMut(&mut HumanVsMachineMatch) -> futures::future::BoxFuture<Box<dyn Any + Send>> + Send>;

Then defined execute method, which will send the CallbackFunction to a channel, and there is a dedicated thread will execute it.

pub async fn execute<T>(func: CallbackFunction) -> Box<T>
where
    T: 'static,
{
    let (sender, receiver) = oneshot::channel();
    let tx = SENDER.lock().unwrap().clone();
    let tx = tx.expect("Sender must not be None");
    if let Err(e) = tx.send((func, sender)) {
        panic!("Unable to send callback function on channel. {}", e);
    }
    let ret = receiver.await.expect("Unable to receive");
    ret.downcast::<T>().expect("BUG! Unable to cast")
}

All above works fine. but I have problem to call the execute method.

let board_info: Box<BoardInfo> =
       execute(Box::new(move |m: &mut HumanVsMachineMatch| {
            Box::pin(async {
                if !black {
                    m.machine_move();
                }

                m.get_board()
            })
        }))
        .await;

type mismatch resolving <impl futures::Future<Output = BoardInfo> as futures::Future>::Output == Box<(dyn std::any::Any + std::marker::Send + 'static)>
expected struct Box<(dyn std::any::Any + std::marker::Send + 'static)>
found struct BoardInfo
required for the cast to the object type dyn futures::Future<Output = Box<(dyn std::any::Any + std::marker::Send + 'static)>> + std::marker::Send

Please how should I solve the error?

You've declared the callback return type as Box<dyn Any + Send>, but the closure is actually returning m.get_board() of type BoardInfo. There's no Box around the BoardInfo returned, so it doesn't match and can't be coerced to Box<dyn Any>. Changing the result to Box::new(m.get_board()) should help.

That said, do you really need dyn Any there? That isn't a necessary part of accepting a dynamic async callback, and it'd be easy to use generics there:

type CallbackFunction<T> =
    Box<dyn FnMut(&mut HumanVsMachineMatch) -> futures::future::BoxFuture<T> + Send>;

pub async fn execute<T>(func: CallbackFunction<T>) -> Box<T>
1 Like

Thanks @kpreid

Generic does not work for me at this point because there could be multiple types for T. But I need put them into the same channel. Perhaps I'd better declare an enum to include them all

Changing the result to Box::new(m.get_board()) should help.

execute(Box::new(move |m: &mut HumanVsMachineMatch| {
            Box::pin(async {
                if !black {
                    m.machine_move();
                }

                Box::new(m.get_board())
            })
        }))
        .await

Tried that and it does not help

expected struct Box<(dyn std::any::Any + std::marker::Send + 'static)>
found struct Box<BoardInfo>

Try adding as Box<dyn Any + Send> after Box::new(m.get_board()). It is possible that the coercion from Box<BoardInfo> to Box<dyn Any + Send> here didn't trigger.