`Async Closure` problem

I have following code:

pub trait ConnImpl: Sized {
    fn conn_callback(self) -> impl Future<Output = impl AsyncFnMut(&[u8]) -> Result<()> + Send> + Send;
}

impl ConnImpl for Connection {
    async fn conn_callback(self) -> impl AsyncFnMut(&[u8]) -> Result<()> + Send {
        let (mut sender, mut recv) = self.accept_bi().await.unwrap();
        let ct_str = recv.read_into().await.unwrap();
        async move |data| {
            sender.write_all(data).await?;
            Ok(())
        }
    }
}
pub trait HandleConnection2<T> {
    fn handle_connection2(self, conn: T) -> impl Future<Output = Result<()>> + Send;
    // async fn handle_connection2(self, conn: T) -> Result<()>;
}

pub struct NodeStruct;

impl HandleConnection2<Connection> for NodeStruct {
    async fn handle_connection2(self, conn: Connection) -> Result<()> {
        let mut callback = conn.conn_callback().await;
        let simu_msg = vec![1, 2];
        callback(&simu_msg).await.unwrap();
        Ok(())
    }
}

And I got this compiling error when I impl HandleConnection2<Connection> for NodeStruct:

error: future cannot be sent between threads safely
  --> data-server/src/interaction.rs:56:5
   |
56 |     async fn handle_connection2(self, conn: T) -> Result<()> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `handle_connection2` is not `Send`
   |
   = note: the full name for the type has been written to '/root/qust/target/debug/deps/data_server-7a2d8a92fe304fc1.long-type-12962579465712343202.txt'
   = note: consider using `--verbose` to print the full type name to the console
   = help: within `impl futures::Future<Output = std::result::Result<(), anyhow::Error>>`, the trait `std::marker::Send` is not implemented for `<impl (AsyncFnMut(...) -> ...) + Send as AsyncFnMut<...>>::CallRefFuture<'_>`
note: future is not `Send` as it awaits another future which is not `Send`
  --> data-server/src/interaction.rs:67:13
   |
67 |             callback(&msg).await.unwrap();
   |             ^^^^^^^^^^^^^^ await occurs here on type `<impl (for<'a> AsyncFnMut(&'a [u8]) -> std::result::Result<(), anyhow::Error>) + std::marker::Send as std::ops::AsyncFnMut<(&[u8],)>>::CallRefFuture<'_>`, which is not `Send`
note: required by a bound in `interaction::HandleConnection2::{synthetic#0}`
  --> data-server/src/interaction.rs:37:80
   |
37 |     fn handle_connection2(self, conn: T) -> impl Future<Output = Result<()>> + Send;
   |                                                                                ^^^^ required by this bound in `HandleConnection2::{synthetic#0}`
help: consider further restricting the associated type
   |
56 |     async fn handle_connection2(self, conn: T) -> Result<()> where <impl (for<'a> AsyncFnMut(&'a [u8]) -> std::result::Result<(), anyhow::Error>) + std::marker::Send as std::ops::AsyncFnMut<(&[u8],)>>::CallRefFuture<'_>: std::marker::Send {
   |                                                              +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

I have marked that callback is Send in the trait ConnImpl signature fn conn_callback(self) -> impl Future<Output = impl AsyncFnMut(&[u8]) -> Result<()> + Send> + Send;, why the compiler still said it is not Send?

I can simplify this problem:

struct Acs;

impl Acs {
    fn get_closure(self) -> impl AsyncFnOnce(&[u8]) -> Result<()> {
        async move |data| {
            Ok(())
        }
    }
}

fn test_fn() {
    tokio::spawn(async move {
        let a = Acs;
        let cb = a.get_closure();
        let data = vec![1, 2, 3];
        cb(&data).await;
    });
}

It has error like above.
So my problem maybe like this: why a async clousre cannot be send to another thread?

The problem is that even though the AsyncFnMut itself is Send, the future that it generates isn’t. There’s no way to express the desired trait bound currently – in general you should stick to only using AsyncFn* traits in argument position, as this limitation makes their use in return position not as useful. So you will likely just have to rearchitect your code to avoid AsyncFnMut.

2 Likes

Or rather, the future isn't guaranteed to be Send, even if the actual instance would be. Such auto-traits are implicitly leaked for the impl AsyncFnOnce itself, but not its associated types.

2 Likes