Downcasting a value stored in HashMap and returning Option<&T>

Hi all, I would really appreciate it if someone could take a look at the code below and tell me why the code doesn't work? Also what can I do to get this to work.

Short summary of what I am trying to achieve:

  1. I have a trait for example SomeTrait
  2. I have a struct Bar which contains a field data which is HashMap<String, Box>

I am writing a method get<T> for Bar which should access the stored value and return Option<&T>

Following is the example code I have:

EDIT: Fixed the link

So my question is if there is an ideal way to fix this? Preferably by not creating a new owned value?

You’re missing the link :wink: Make sure to use the actual “Permalink to the playground” from the “Share” menu and copy from there.

I think your Playground link is wrong.

Hi @steffahn , @Redglyph . Thanks for flagging that! I have fixed the link

Any being implemented for almost all types makes it hard to call its methods (and therefore your subtrait's methods) properly sometimes. value is &Box, so you need to deref it twice.

fn get<T: 'static>(&self, name: &str) -> Option<&T> {
    match self.data.get(name) {
        Some(value) => (&**value).as_any().downcast_ref::<T>(),
        _ => None,
    }
}

PS: (*value).as_any() also works but is a bit less intuitive.

1 Like

In cases where method lookup might give you the wrong result, you can write more robust, and debatably clearer, code by avoiding method calls:

use std::ops::Deref;

...

<dyn SomeTrait>::as_any(Box::deref(value)).downcast_ref::<T>()

By mentioning dyn SomeTrait as the expected receiver type, we actually stop as_any() from being called on any of the regular implementations, because all the other implementations require Clone, and Box<dyn SomeTrait> doesn't implement clone.

(I don't ordinarily recommend calling Deref::deref() explicitly like this, but in this case it provides some useful disambiguation about what is expected to be dereferenced at this particular site.)


However, also note that since Rust 1.86 has been released, with trait upcasting support, it is not necessary to have an as_any() method at all. You can delete it and just write

<dyn Any>::downcast_ref::<T>(&**value)

Or, qualified:

<dyn Any>::downcast_ref::<T>(Box::<dyn SomeTrait>::deref(value))
3 Likes

Thanks a lot for the details @kpreid ! That works and also gives me some clarity on why &**value is required.