Rust cannot send unwrapped Result data across await point

Viewed 102

Consider the following code:

use std::error::Error;

use tokio::sync::mpsc;

fn gen_data() -> Result<i32, Box<dyn Error>> {
    Ok(42)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let (tx, rx) = mpsc::channel(10);

    // TODO: future cannot be sent between threads safely the trait `Send` is not implemented for `dyn std::error::Error`
    tokio::spawn(async move {
        if let Ok(data) = gen_data() {
            tx.send(data).await;
        }
    });

    while let Some(res) = rx.recv().await {
        println!("{}", res);
    }

    Ok(())
}

I have a general sense of what the error is trying to say. Is it saying the Err result of my gen_data function is not being dropped before I'm awaiting?

I do not need the error data from my function. The logic I'm trying to achieve is to do nothing if the gen_data returned an error, if gen_data works, then send it.

How can I refactor the code to get it to compile?

1 Answers

In real-world applications, you should probably avoid using a Box<dyn Error> and instead create a custom error enum that can represent every error your application may occur. See for instance, as an example, this post explaining how to achieve that. So, if you are thinking about refactoring your code, this might be the right time to change how you handle errors.

However, if you want to keep your error as trait objects, you just have to enforce them to be Send by changing Box<dyn Error> to Box<dyn Error + Send>. Similarly, if you need them to be also Sync, go with Box<dyn Error + Send + Sync>. The only issue of this approach is that this constraint will propagate through the rest of your code: if at one moment a function only returns a Box<dyn Error>, then you can't get a Box<dyn Error + Send> back, so even regular (non-async) functions will have to return errors that are also Send, which is a bit weird.

Related