Using the `Result<T, Box<dyn Error>>` trick in Rust, but across multiple threads

Viewed 918

It's a common trick to annotate your functions with returning Result<X, Box<dyn Error>> to allow them to return any error at all. However, you can't return this from a thread without the error itself implementing Send. For example this code:

use rayon::prelude::*; // 1.5.1
use std::error::Error;

fn main(){
    ["1", "2", "three"]
        .into_par_iter()
        .try_for_each(|i| -> Result<usize, Box<dyn Error>> {
            let inner = i.parse::<usize>()?;
            Ok(inner)
        }
    );
}

Gives this error:

error[E0277]: `dyn std::error::Error` cannot be sent between threads safely

(playground)


On the other hand, if you try to specify that the error must implement Send, the ? operator no longer works:

use rayon::prelude::*; // 1.5.1
use std::error::Error;

fn main(){
    ["1", "2", "three"]
        .into_par_iter()
        .try_for_each(|i| -> Result<usize, Box<dyn Error + Send>> {
            let inner = i.parse::<usize>()?;
            Ok(inner)
        }
    );
}
error[E0277]: `?` couldn't convert the error to `Box<dyn std::error::Error + Send>`

(playground)

How can I continue to use the Box<dyn Error> shortcut, but only for errors that can be sent, allowing it to work across threads?

1 Answers

The implemention in std you are looking for is this:

impl<'a, E: Error + Send + Sync + 'a> From<E> for Box<dyn Error + Send + Sync + 'a>

Change your closure's return type to Result<usize, Box<dyn Error + Send + Sync>>, fix the unrelated type error, and it will compile.

Related