use std::{error::Error, thread};
fn main() {
let thread = thread::spawn(move || {
let try = || -> Result<(), Box<dyn Error>> {
let mut res = 0;
for _ in 0..7 {
res += 2;
if res > 4 {
return Err("too much".into());
}
}
Ok(())
};
if try().is_err() {
println!("err in thread processing")
}
});
thread.join();
println!("thread ended")
}
it works great, however if I want to analyze the error in a caller thread, there is no such function as:
println!("error in thread {}", thread.get_err());
AI recommend to use a channel to get a thread error in the caller thread, however I do not want to introduce a channel just for errors. Which approach do you use?
Your approach looks so good to be true, tried it and met the problem:
error[E0277]: `dyn std::error::Error` cannot be sent between threads safely
--> /media/exhdd/Dev/modu/thread/thread.rs:3:18
|
3 | let thread = thread::spawn(move || {
| __________________^
4 | | let try = || -> Result<(), Box<dyn Error>> {
5 | | let mut res = 0;
6 | | for _ in 0..7 {
... |
14 | | try()
15 | | });
| |______^ `dyn std::error::Error` cannot be sent between threads safely
|
= help: the trait `Send` is not implemented for `dyn std::error::Error`
= note: required for `std::ptr::Unique<dyn std::error::Error>` to implement `Send`
note: required because it appears within the type `Box<dyn std::error::Error>`
--> /rustc/ac7f9ec7da74d37fd28667c86bf117a39ba5b02a/library/alloc/src/boxed.rs:234:11
note: required because it appears within the type `Result<(), Box<dyn std::error::Error>>`
--> /rustc/ac7f9ec7da74d37fd28667c86bf117a39ba5b02a/library/core/src/result.rs:557:9
note: required by a bound in `spawn`
--> /rustc/ac7f9ec7da74d37fd28667c86bf117a39ba5b02a/library/std/src/thread/functions.rs:125:0
error: aborting due to 1 previous error
How to solve it? Maybe converting the error to String?
Most types are automatically Send, so it generally doesn't restrict you much.
If you aren't already, try using anyhow instead of dyn Error, it gives you a bunch of nicer ways to handle generic errors (a good compliment to thiserror for implementing Error for specific types)
If you use the suggested Box<dyn Error + Send>, you are saying you accept types that implement both Error and Send, so it works as the return type of the thread function. Most types automatically implement Send (it's an "auto trait" implemented on any type that doesn't explicitly opt out or contain a type that isn't Send), so most types that implement Error also implement Send.
Nice, it seems working. One problem I do not know how to resolve. I am trying to re-throw an exception I got in the thread.
...
let res = thread.join();
match res {
Ok(_) => (),
Err(err) => match err.downcast_ref::<io::Error>() {
Some(err) => return Err(err.clone()), // !!! no clone error
_ => (),
},
}
However, an owner of the exception is the thread, and I can't move the object from and then return it. clone doesn't exist for the exception either. But I believe I overlook some obvious solution.
Yeah, it's a bit annoying that io::Error doesn't implement Clone.
But you actually have a different error here first: if you return a Result from your thread function, join actually returns two layers of Result, first a result for the thread itself failing (likely due to a panic that you should resume_unwind), then the actual returned value of a Result. It's just unfortunate the thread Result is also a Box (of Any), rather than a clearer type.
Even with that handled, though, you will just get back to the same problem, it's not that thread still owns the value (it just exited, it doesn't own anything!) - it's that you're using downcast_ref on the Any trait, which returns a borrowed reference to the value.
Since you're using a Box, you could instead use just downcast but because that requires moving from the box you likely need to handle the case where it didn't match to put the value back or something, notice it gives you the original value as the "Err" value.
But, if you know the error value will always be of type io::Error, then you could simply make the thread return that as the error type, and not require any Box at all!