Why does downcasting error behind a Box return None?

#1

Please could someone explain why if an error is put behind a Box, downcast_ref::<MyError>() returns None? I don’t really gain understanding from the Box or Error documentation.

Here’s a snippet (playground):

use std::{error::Error, fmt::{self, Display, Formatter}};

// === //

#[derive(Debug)]
struct MyError;
impl Display for MyError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "MyError") }
}

impl Error for MyError {}

// === //

#[derive(Debug)]
struct Inner<T: ?Sized> {
    error: T,
}

#[derive(Debug)]
pub struct ErrorWrapper {
    inner: Box<Inner<dyn Error + Send + Sync>>,
}

impl ErrorWrapper {
    pub fn new<E>(error: E) -> Self
    where
        E: 'static + Error + Send + Sync,
    {
        Self {
            inner: Box::new(Inner {
                error: Box::new(error),
            }),
        }
    }

    pub fn as_error(&self) -> &(dyn Error + 'static) {
        &self.inner.error
    }
}

fn main() {
    let my_error = MyError;
    let error: &dyn Error = &my_error;
    println!("{:?}", error.downcast_ref::<MyError>());

    let wrapper = ErrorWrapper::new(MyError);
    println!("{:?}", wrapper.as_error().downcast_ref::<MyError>());
}

Output:

Some(MyError)
None
#2

Your error type is actually Box<MyError>, not simply MyError. Note that ErrorWrapper’s field is Box<Inner<dyn Error + Send + Sync>> but you assign to it as error: Box::new(error) rather than just error: error.

If you remove the box around error, things work: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=70e8b47839507f06fd52483136932eee

Alternatively, if you change the inner field to Inner<Box<dyn Error + Send + Sync>>, things behave as expected as well: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a9b5c859a81342460c9840dc528784cf

1 Like
#3

Ah, my eyes couldn’t see!
Thank you :bowing_man:!