"future cannot be sent between threads safely" error

79 Views Asked by At

I have the following code I have some compilation problems with.

pub trait ATrait {
    async fn afunc(&self, x: u32) -> u32;
}
struct ATraitWrapper<T: ATrait + Send + Clone + 'static> {
    inner: T,
}
impl<T: ATrait + Send + Clone + 'static> ATrait for ATraitWrapper<T> {
    async fn afunc(&self, x: u32) -> u32 {
        let (tx, rx) = std::sync::mpsc::channel();
        let inner_clone = self.inner.clone();
        tokio::spawn(async move {
            let result = inner_clone.afunc(x).await;
            tx.send(result).expect("Failed to send through channel")
        });
        rx.recv().expect("Failed to receive from channel")
    }
}

pub struct MyStruct {
    value: u32,
}

impl ATrait for MyStruct {
    async fn afunc(&self, x: u32) -> u32 {
        self.value + x
    }
}

The error I get is on tokio::spawn:

error: future cannot be sent between threads safely
   --> src/lib.rs:11:9
    |
11  |         tokio::spawn(async move {
    |         ^^^^^^^^^^^^ future created by async block is not `Send`
    |
    = help: within `{async block@src/lib.rs:11:22: 14:10}`, the trait `Send` is not implemented for `impl Future<Output = u32>`
note: future is not `Send` as it awaits another future which is not `Send`
   --> src/lib.rs:12:26
    |
12  |             let result = inner_clone.afunc(x).await;
    |                          ^^^^^^^^^^^^^^^^^^^^ await occurs here on type `impl Future<Output = u32>`, which is not `Send`
note: required by a bound in `tokio::spawn`
   --> /playground/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.36.0/src/task/spawn.rs:166:21
    |
164 |     pub fn spawn<F>(future: F) -> JoinHandle<F::Output>
    |            ----- required by a bound in this function
165 |     where
166 |         F: Future + Send + 'static,
    |                     ^^^^ required by this bound in `spawn`
help: `Send` can be made part of the associated future's guarantees for all implementations of `ATrait::afunc`
    |
2   -     async fn afunc(&self, x: u32) -> u32;
2   +     fn afunc(&self, x: u32) -> impl std::future::Future<Output = u32> + Send;
    |

The type of the closure is Future<Output=()>, why can't it be Send?

1

There are 1 best solutions below

2
cafce25 On BEST ANSWER

The problem is, that one can write valid implementations of ATrait that do not produce a Send future from afunc for example:

impl ATrait for () {
    async fn afunc(&self, x: u32) -> u32{
        // holding `y` a `!Send` `Rc` makes the `Future` returned
        // by this function `!Send`
        let y = std::rc::Rc::new(());
        std::future::ready(()).await;
        *y;
        x
    }
}

and your code being generic has to work for every possible implementation (even nonsensical ones like my example).

The compiler aready suggests a fix for this, that disallows such definitions use

    fn afunc(&self, x: u32) -> impl Future<Output = u32> + Send;

as your method signature instead.