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:
- I have a trait for example
SomeTrait
- 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
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.