Fn -> Future is not Send

Hi,

I'm passing a Fn as a parameter in async/.await:

async fn foo<F, S> (handler: F) 
    where 
    F: Fn(Request<()>) -> S + Send + Clone + 'static,
    S: Future<Output = Response<()>> + Send + 'static,
{
    <snip>
    loop {
       if (condition) {
           bar(&handler).await;
       }
    }
    <snip>
}

When I spawn foo() into a task, I got an error saying:

future returned by `foo` is not `Send`

and indicated handler was not Send, and across .await when calling bar.

My question is: why handler is not Send even when its type F has Send in it?

Thanks.

The handler is indeed Send, but not Sync. In this code the reference to the handler is alive across the .await point which means the future returned by foo will store the reference. To make the future Send, the reference to the handler should also be Send, which requires the handler be Sync.

2 Likes

In case you are not familiar with it, an immutable reference to something is Send if that something is Sync. Your thing is not Sync, so the immutable reference is not Send.

2 Likes

Thanks! Indeed when I use handler F when calling bar, the original error is gone, then I encountered a similar error inside bar. (I was playing with F and &F back and forth.)

I snipped part of foo as it seems this forum does not allow a long program.

async fn foo<F, S> (handler: F) 
<snip>
       if (condition) {
           let handler_clone = handler.clone();
           bar(handler_clone).await;
       }
    }
    <snip>
}

async fn bar<F, S>(handler: F) 
    where 
    F: Fn(Request<()>) -> S + Send + Clone + 'static,
    S: Future<Output = Response<()>> + Send + 'static,
{
    loop {
        let req_handler = handler.clone();

         executor.spawn({
              let response = req_handler(req).await;
              respond(response)
         }).detach();
    }
}

The error is reported when calling req_handler :

565 |                         let response = req_handler(req).await;
    |                                        -----------           ^
    |                                        |
    |                                        has type `&F` which is not `Send`

For some reason, rustc complains it's &F even when handler is F and reg_handler is a clone. I cannot understand why.

Regarding your last snippet, something is up. You're accessing some variable req in bar, which is not defined anywhere? And the error message you posted is incomplete. Can you post correct code with full error message please?

sorry, my program is still a mess, but here are the related parts, hopefully:

my_executor.spawn({
    foo(handler, &my_executor)
});

then

async fn foo<F,S> (handler: F, executor: &smol::Executor<'_>,)
where <snip>
{
   loop {
         <snip>
          let handler_clone = handler.clone();
          bar(handler_clone, executor).await;
   }
<snip>
}

async fn bar<F,S> (handler: F,  executor: &smol::Executor<'_>)
where <snip>
{
    loop {
        let (rx, tx) = channel::unbounded();
        let req = create_request(rx, ...);
        <snip>

        let req_handler = handler.clone();

        executor.spawn({
              let response = req_handler(req).await;
              respond(response)
         }).detach();      
    }
}

The error is:

      foo(handler, &my_executor)
    });
    | |_____________^ future returned by `foo` is not `Send`

564 |                         let response = req_handler(req).await;
    |                                        ^^^^^^^^^^^^^^^^^^^^^^ first, await occurs here, with `req_handler` maybe used later...
note: `req_handler` is later dropped here
    |
564 |                         let response = req_handler(req).await;
    |                                        -----------           ^
    |                                        |
    |                                        has type `&F` which is not `Send`
help: consider moving this into a `let` binding to create a shorter lived borrow
    |
564 |                         let response = req_handler(req).await;
    |                                        ^^^^^^^^^^^^^^^^
help: consider further restricting this bound
    |
167 |     F: Fn(Request<()>) -> S + Send + Clone + 'static + Sync,

Just to follow up, I got extra help from another forum (discord), and it turns out I can use FnOnce when calling the function. (req_handler above).

So I extracted the final closure into a function that takes in FnOnce, and that did the trick.

I didn't know that FnOnce takes the ownership, and Fn is always called using a reference. Thanks!