jeanluc
jeanluc

Reputation: 1708

What is the difference between `future` and `async move { future }`?

I'm wondering why changing the code from using a Future directly to using it in an async move block makes a difference. Passing it to tokio::spawn directly yields an error:

use std::error::Error;
use tokio::sync::{mpsc::{self, Receiver}, oneshot::Sender};

struct Msg {
    resp: Sender<Vec<u8>>,
}

async fn client_thread(
    mut rx: Receiver<Msg>,
) -> Result<(), Box<dyn Error>> {
    while let Some(msg) = rx.recv().await {
        msg.resp
            .send(vec![]) // return some data
            .unwrap();
    }
    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let (_tx, rx) = mpsc::channel(5);
    
    tokio::spawn(client_thread(rx));

    Ok(())
}
error[E0277]: `(dyn std::error::Error + 'static)` cannot be sent between threads safely
   --> src/main.rs:23:5
    |
23  |     tokio::spawn(client_thread(rx)); // <--- Difference is here
    |     ^^^^^^^^^^^^ `(dyn std::error::Error + 'static)` cannot be sent between threads safely
    |
    = help: the trait `Send` is not implemented for `(dyn std::error::Error + 'static)`
    = note: required because of the requirements on the impl of `Send` for `Unique<(dyn std::error::Error + 'static)>`
    = note: required because it appears within the type `Box<(dyn std::error::Error + 'static)>`
    = note: required because it appears within the type `Result<(), Box<(dyn std::error::Error + 'static)>>`
note: required by a bound in `tokio::spawn`
   --> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.21.0/src/task/spawn.rs:128:20
    |
128 |         T::Output: Send + 'static,
    |                    ^^^^ required by this bound in `tokio::spawn`

But changing it to an async block makes it compile:

tokio::spawn(async move { client_thread(rx) });

The return type of client_thread is exactly the same as the main function, however, which runs with Tokio without any problems. Moreover, the error type from reqwest implements Send.

Upvotes: 2

Views: 770

Answers (1)

kmdreko
kmdreko

Reputation: 60457

Wrapping the Future in an async {} block as you've done makes it compile but doesn't actually run the Future. You need to .await it:

use futures::executor::block_on; // 0.3.12

async fn f(s: &str) {
    println!("ran {}", s);
}

fn main() {
    block_on(f("first"));
    block_on(async { f("second") }); // this doesn't print
    block_on(async { f("third").await });
}
ran first
ran third

Since the future is not ran, it doesn't end up affecting the traits for the async {} block and thus can can satisfy the Send constraint. You'll have the same issue again with .await.


In your case, all you need to do is ensure that the future implements Send so it can be ran with tokio::spawn. The fix is to dictate that the Error trait being returned implements Send:

async fn client_thread(
    mut rx: Receiver<ClientMsg>,
) -> Result<(), Box<dyn Error + Send>> {
    // ...                    ^^^^^^
}

Upvotes: 1

Related Questions