Home > other >  How to deal with non-Send futures in a Tokio spawn context?
How to deal with non-Send futures in a Tokio spawn context?

Time:01-23

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.

  • Related