This simple program yields a compiler error:
#[tokio::main]
async fn main() {
tokio::spawn(async {
foo().await;
});
}
async fn foo() {
let f1 = bar();
let f2 = bar();
tokio::join!(f1, f2);
}
async fn bar() -> Result<(), Box<dyn std::error::Error>> {
println!("Hello world");
Ok(())
}
error[E0277]: `(dyn std::error::Error + 'static)` cannot be sent between threads safely
--> src/main.rs:5:18
|
5 | tokio::spawn(async {
| _____------------_^
| | |
| | required by a bound introduced by this call
6 | | foo().await;
7 | | });
| |_____^ `(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 for `Unique<(dyn std::error::Error + 'static)>` to implement `Send`
= note: required because it appears within the type `Box<dyn Error>`
= note: required because it appears within the type `Result<(), Box<dyn Error>>`
= note: required because it appears within the type `MaybeDone<impl Future<Output = Result<(), Box<dyn Error>>>>`
= note: required because it appears within the type `(MaybeDone<impl Future<Output = Result<(), Box<dyn Error>>>>, MaybeDone<impl Future<Output = Result<(), Box<dyn Error>>>>)`
= note: required because it captures the following types: `ResumeTy`, `impl Future<Output = Result<(), Box<dyn Error>>>`, `(MaybeDone<impl Future<Output = Result<(), Box<dyn Error>>>>, MaybeDone<impl Future<Output = Result<(), Box<dyn Error>>>>)`, `&mut (MaybeDone<impl Future<Output = Result<(), Box<dyn Error>>>>, MaybeDone<impl Future<Output = Result<(), Box<dyn Error>>>>)`, `u32`, `[[email protected]:95:17]`, `PollFn<[[email protected]:95:17]>`, `()`
note: required because it's used within this `async fn` body
I don't really understand what the error means. When I remove the return type of the bar function it works, but what is the actual error here?
Box<dyn Error>is an opaque type. It may contain any type.Suppose it contains a type that cannot be safely send to a different thread than the one it was created in. For example, a
MutexGuard, that must release the mutex when dropped from the same thread as it acquired it. Or anRc, that decrements the reference count on drop non-atomically, and thus can cause a data race on drop if moved to another thread. Then we should not send it to another thread. We say the type does not implementSend. BecauseBox<dyn Error>may contain such types, it itself does not implementSend.However,
tokio::spawn()may move the future between threads between.awaitpoints. This is to improve efficiency:tokiouses a work-stealing scheduler, meaning it will move tasks to threads (and therefore CPU cores) that are less busy. But suppose theBox<dyn Error>would contain a type that does not implementSend, for exampleMutexGuard, it would be created before thetokio::join!()call, in thread A, and dropped after it, potentially in thread B! (becausetokio::join()has an implicit.await). This mean this is not safe, therefore the future offoo()is notSendeither, and you cannot spawn it into a task.The fix is simple: ensure the
Box<dyn Error>isSend. This can be done by adding a+ Sendbound to it: