In this lesson, you will learn how to transport errors from independent worker threads back to the main thread so you can handle them centrally.
When you spawn a thread using std::thread::spawn, the code inside that thread doesn't have a direct way to return an Result to the main function because the spawn signature expects a closure that returns the thread's value. If a task fails, simply letting the thread end silently or panic isn't enough; we need a reliable way to catch that failure.
The JoinHandle Pattern
When you spawn a thread, it returns a JoinHandle<T>. When you call .join() on this handle, it returns a Result<T, Box<dyn Any + Send>>.
If your thread logic returns a Result<T, MyError>, the JoinHandle will return a Result<Result<T, MyError>, Box<dyn Any>>. The outer Result handles the case where the thread panicked, while the inner Result contains the actual domain error you generated.
Here is how you safely propagate that error:
use std::thread;
fn main() {
let handle = thread::spawn(|| -> Result<i32, String> {
// Imagine some complex operation here
Err("Something went wrong in the worker!".to_string())
});
// We join the thread and handle the two-tier error structure
match handle.join() {
Ok(Ok(value)) => println!("Success: {}", value),
Ok(Err(e)) => println!("Worker failed with error: {}", e),
Err(_) => println!("Worker thread panicked!"),
}
}
Why this matters
By wrapping our result in a Result inside the thread, we distinguish between a "logic failure" (the operation failed) and a "runtime failure" (the thread crashed/panicked). Because Result implements the Send trait, it can be safely passed across thread boundaries as long as the error type itself is also Send.
This pattern allows you to treat worker threads as fallible components of your system, ensuring that an error in a background task doesn't vanish, but instead becomes a value that the main thread can inspect, log, or recover from.
Key Takeaway: Propagate errors from worker threads by returning a Result from the thread closure, then unpack the JoinHandle to handle both potential panics and expected domain errors.