I think my question relates to Rust Issue 57017.
The following code does not compile and produces error: future cannot be sent between threads safely
due to future created by async block is not 'Send'
originating in async fn execute_fail
. On the other hand async fn execute_work
compiles and has the same functions and logic as execute_fail
but in a less elegant manner.
async fn stream() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
async fn process() {}
async fn execute_fail() -> Result<(), Box<dyn std::error::Error>> {
match stream().await {
Err(err) => {
return Err(err);
}
Ok(()) => {
process().await;
}
}
Ok(())
}
async fn execute_work() -> Result<(), Box<dyn std::error::Error>> {
let mut is_success = false;
match stream().await {
Err(err) => {
return Err(err);
}
Ok(()) => {
is_success = true;
}
}
if is_success {
process().await;
}
Ok(())
}
async fn my_fn() {
tokio::spawn(async {
if let Err(err) = execute_fail().await {
panic!("error here");
}
});
tokio::spawn(async {
if let Err(err) = execute_work().await {
panic!("error here");
}
});
}
Gives:
error: future cannot be sent between threads safely
--> src/lib.rs:39:5
|
39 | tokio::spawn(async {
| ^^^^^^^^^^^^ future created by async block is not `Send`
|
::: /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.11.0/src/task/spawn.rs:127:21
|
127 | T: Future + Send + 'static,
| ---- required by this bound in `tokio::spawn`
|
= help: the trait `Send` is not implemented for `dyn std::error::Error`
note: future is not `Send` as this value is used across an await
--> src/lib.rs:15:13
|
10 | match stream().await {
| -------------- has type `Result<(), Box<dyn std::error::Error>>` which is not `Send`
...
15 | process().await;
| ^^^^^^^^^^^^^^^ await occurs here, with `stream().await` maybe used later
16 | }
17 | }
| - `stream().await` is later dropped here
I would like an explanation why execute_fail
does not compile where as execute_work
does. The compiler note note: future is not
Send as this value is used across an await
doesn't make sense to me as I don't see how any values are being used across an await.
I would like an explanation why execute_fail does not compile where as execute_work does.
When you match stream().await
it essentially works as:
let v = stream().await;
match v {
...
}
So v
is alive for the entirety of the match, meaning as far as the compiler is concerned its "life" overlaps with the process().await
call. Values must be Send
to live through an await
call, as the coroutine may be resumed on a different thread than it was suspended.
Of note: the code here can be significantly simplified:
async fn execute_fail() -> Result<(), Box<dyn std::error::Error>> {
if let Err(err) = stream().await {
return Err(err);
}
process().await;
Ok(())
}
or even
async fn execute_fail() -> Result<(), Box<dyn std::error::Error>> {
stream().await?;
process().await;
Ok(())
}