Implementation of `Send` is not general enough for AsyncFnMut

I have the following minimal example using an async closure:

pub struct MyStruct {}

impl MyStruct {
    async fn method(&self) {}
}

struct MyObj {}

impl MyObj {
    pub async fn outer(&self, my_struct: &mut MyStruct) {
        let mut my_closure = async || {
            my_struct.method().await;
        };
        self.inner(&mut my_closure).await
    }

    pub async fn inner(&self, my_closure: &mut impl AsyncFnMut()) {
        my_closure().await;
    }
}

#[tokio::main]
async fn main() {
    tokio::spawn(async move {
        let mut my_struct = MyStruct {};
        let my_obj = MyObj {};
        my_obj.outer(&mut my_struct).await;
    })
    .await
    .unwrap();
}

I get the following error on Rust 1.85.0:

error: implementation of `Send` is not general enough
  --> src/main.rs:24:5
   |
24 | /     tokio::spawn(async move {
25 | |         let mut my_struct = MyStruct {};
26 | |         let my_obj = MyObj {};
27 | |         my_obj.outer(&mut my_struct).await;
28 | |     })
   | |______^ implementation of `Send` is not general enough
   |
   = note: `Send` would have to be implemented for the type `&'0 MyStruct`, for any lifetime `'0`...
   = note: ...but `Send` is actually implemented for the type `&'1 MyStruct`, for some specific lifetime `'1`

Is there a possibility to fix this?

1 Like

The error message is wrong. The problem is not anything to do with MyStruct, but rather that,

  • tokio::spawn() requires the provided future to be Send, but
  • my_closure: &mut impl AsyncFnMut() does not constrain the future it produces to be Send.

Unfortunately, there is no way to write such a bound using the newly introduced AsyncFnMut trait. You can do it, however, with pre-1.85 techniques like the async_fn_traits library; this compiles:

[dependencies]
async_fn_traits = "0.1.1"
    pub async fn inner(
        &self,
        my_closure: &mut impl async_fn_traits::AsyncFnMut0<OutputFuture: Send>,
    ) {
        my_closure().await;
    }
1 Like

I tried my best.

  1. give up AsyncFn
pub struct MyStruct {}

impl MyStruct {
    async fn method(&self) {}
}

pub struct MyObj {}

impl MyObj {
    pub async fn outer(&self, my_struct: &MyStruct) {
        let my_closure = async move || {
            my_struct.method().await;
        };
        self.inner(my_closure).await;
    }

    pub async fn inner<F, Fut>(&self, my_closure: F)
    where
        F: Fn() -> Fut,
        Fut: std::future::Future<Output = ()>,
    {
        my_closure().await;
    }
}

#[tokio::main]
async fn main() {
    tokio::spawn(MyObj {}.outer(&MyStruct {})).await.unwrap();
}

  1. try move the ownership of MyStruct
pub struct MyStruct {}

impl MyStruct {
    async fn method(&self) {}
}

pub struct MyObj {}

impl MyObj {
    pub async fn outer(&self, my_struct: MyStruct) {
        let my_closure = async move || {
            my_struct.method().await;
        };
        self.inner(my_closure).await;
    }

    pub async fn inner<F>(&self, my_closure: F)
    where
        F: AsyncFn(),
    {
        my_closure().await;
    }
}

#[tokio::main]
async fn main() {
    tokio::spawn(MyObj {}.outer(MyStruct {})).await.unwrap();
}

Consequently AsyncFn is unusable for now, if we require Send?

As far as I know, yes.

I cannot move MyStruct and need the reference. This is only a minimal example from a larger system.