liveBook · Manning

Downcasting

As explained in Learn Rust in a Month of Lunches

Downcasting is the attempted conversion of a dynamically typed or trait-object value back to a specific concrete type.

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

How Learn Rust in a Month of Lunches covers it

How deep it goes
Explains trait-object type erasure and demonstrates downcast_ref across two error types, while not developing downcast ownership details.
Examples
Uses boxed MyError and RecvError values, then checks them with downcast_ref and prints the matching type.
What it assumes
Assumes familiarity with Rust traits and Box, while explaining that Box<dyn Error> hides a concrete error type.
What you can do after
Diagnose boxed errors by testing one known concrete type at a time and handling successful matches.
How it connects
Connects downcasting with trait objects, dynamic dispatch, Box, and the Error trait.

Also explained in

Compare how 2 books explain downcasting →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Rust in a Month of Lunches. The section links point to the parts of the book it draws on.