Tokio's spawn can only work with a Send
future. This makes the following code invalid:
async fn async_foo(v: i32) {}
async fn async_computation() -> Result<i32, Box<dyn std::error::Error>> {
Ok(1)
}
async fn async_start() {
match async_computation().await {
Ok(v) => async_foo(v).await,
_ => unimplemented!(),
};
}
#[tokio::main]
async fn main() {
tokio::spawn(async move {
async_start().await;
});
}
The error is:
future cannot be sent between threads safely
the trait `Send` is not implemented for `dyn std::error::Error`
If I understand correctly: because async_foo(v).await
might yield, Rust internally have to save all the context which might be on a different thread - hence the result of async_computation().await
must be Send
- which dyn std::error::Error
is not.
This could be mitigated if the non-Send
type can be dropped before the .await
, such as:
async fn async_start() {
let result;
match async_computation().await {
Ok(v) => result = v,
_ => return,
};
async_foo(result).await;
}
However once another .await
is needed before the non-Send
type is dropped, the workarounds are more and more awkward.
What a is a good practice for this - especially when the non-Send
type is for generic error handling (the Box<dyn std::error::Error>
)? Is there a better type for errors that common IO ops implements (async or not) in Rust? Or there is a better way to group nested async calls?
CodePudding user response:
Most errors are Send
so you can just change the return type to:
Box<dyn std::error::Error Send>
It's also common to have Sync
.