What is the recommended way of propagating different error types in Rust (especialy when their definition doesn't merge)?
The Scenario
Initially, I was curious about how to stop a running thread in Rust. Search suggests it goes through channel communication + conditional.
So, I came up with this code which (1) should loop 5 iterations, (2) with 500 ms sleep in-between iterations, and (3) will stop right after the third iteration.
use std::any::Any;
use std::sync::mpsc;
use std::sync::mpsc::SendError;
use std::thread;
use std::time::Duration;
fn main() -> Result<(), Box<dyn Any + Send>> {
fn some_condition(i: i32) -> bool { 3 == i }
thread::spawn(|| -> Result<(), SendError<bool>> {
let (tx, rx) = mpsc::channel::<bool>();
for i in 0..5 {
if some_condition(i) { tx.send(true)? }
if let Ok(true) = rx.try_recv() { break }
println!("i = {}", i);
thread::sleep(Duration::from_millis(500));
}
Ok(())
}).join()?; // Result<(), SendError<bool>>
// I get away with a warning: which the expected compiler behaviour
Ok(())
}
The Problem
While this code run with the expected behaviour, I have a warning for not using the result resulting from the ? error propagation.
So I was tempted to do ?? instead of just . But then, I got the following compiler warning error:?
error[E0277]: `?` couldn't convert the error to `Box<dyn Any + Send>`
--> src/main.rs:44:53
|
27 | fn main() -> Result<(), Box<dyn Any + Send>> {
| ------------------------------- expected `Box<dyn Any + Send>` because of this
...
44 | }).join()/*.map_err(|error| Reference(error))*/??;
| ^ the trait `From<SendError<bool>>` is not implemented for `Box<dyn Any + Send>`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following implementations were found:
<Box<(dyn std::error::Error + 'a)> as From<E>>
<Box<(dyn std::error::Error + 'static)> as From<&str>>
<Box<(dyn std::error::Error + 'static)> as From<Cow<'a, str>>>
<Box<(dyn std::error::Error + 'static)> as From<String>>
and 22 others
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, SendError<bool>>>` for `Result<(), Box<dyn Any + Send>>`
Then I realised it is because Box<dyn Any + Send> and From<SendError<bool>> are not overlapping types... And, the first propagation match the former type and the second, the latter.
So I suspect from here that the problem could be phrased as: [H]ow to define overlapping types for such use cases?
Envisioned Solution
Intuitively, I tried adding this code that fails (as expected):
// Only traits defined in the current crate can be implemented for arbitrary types [E0117]
impl From<SendError<bool>> for Box<dyn Any + Send> {
fn from(_: SendError<bool>) -> Self {
todo!()
}
}
However, wrapping errors under the same type solves the issue.
Adding:
use crate::Error::{Reference, Source};
#[derive(Debug)]
enum Error {
Reference(Box<dyn Any + Send>),
Source(SendError<bool>)
}
impl From<Error> for SendError<bool> {
fn from(_: Error) -> Self {
todo!()
}
}
impl From<SendError<bool>> for Error {
fn from(_: SendError<bool>) -> Self {
todo!()
}
}
Then, changing lines to match
// ...
tx.send(true).map_err(|error| Source(error))?;
// ...
}).join().map_err(|error| Reference(error))??;
Bottom Line
It works but is so verbose that I's like it for Rust to have another way (something in the likes of Infallible, maybe).
What is the recommended way of propagating different error types in Rust (especialy when their definition doesn't merge)?