Downcasting tries to turn a trait object back into its original concrete type. In Rust, this is useful when Box<dyn Error> hides the specific error you need to inspect or match. section 13.2.3 section 13.3
use std::sync::mpsc::RecvError;
use std::error::Error;
use std::fmt;
enum MyError {
TooMuchStuff,
CantConnect,
NoUserRegistered,
SomethingElse,
}
impl std::error::Error for MyError {}
impl fmt::Display for MyError { #1
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "Wouldn't you like to know...")
}
}
impl fmt::Debug for MyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Lol not telling you what went wrong")
.finish()
}
}
fn give_error_back(is_tru: bool) -> Box<dyn Error> { #2
if is_true {
Box::new(MyError::TooMuchStuff)
} else {
Box::new(RecvError)
}
}
A function can return different concrete errors behind the shared Box<dyn Error> type, leaving the exact error available only through downcasting. section 13.2.4
The Error trait provides downcast(), downcast_ref(), and downcast_mut(). Use downcast_ref() when inspecting a borrowed trait object: it returns Some with the concrete value when the requested type matches, and None otherwise. section 13.2.4
A typical inspection pattern is:
- Store several errors as Box<dyn Error>, so callers can handle different concrete error types through one interface. section 13.2.4
- Try one possible concrete type at a time with err.downcast_ref::<MyError>(). section 13.2.4
- Handle the matching concrete value inside the Some branch, then try another type in an else-if branch. section 13.2.4
Checking two possible errors
for err in errs.iter() { if let Some(my_error) = err.downcast_ref::<MyError>() { println!("Got a MyError!"); } else if let Some(recv_error) = err.downcast_ref::<RecvError>() { println!("Got a RecvError!"); } } The loop identifies each boxed error while preserving the shared Box<dyn Error> return type. section 13.2.4

