Reputation: 1708
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
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