0

Let's say I want to store elements of different types in HashMap but all of them will implement some trait. And I want to be able to retrieve element casted to the right type. The choice of the key allows me to be sure about the type of the element (in the example below type id of the object is a part of the key). How to properly convert found element to T?

use std::collections::HashMap;
use std::any::TypeId;
use std::rc::Rc;

trait MyTrait {
    fn new() -> Self where Self: Sized;

    // Other methods
}

struct MyStruct {
    items: HashMap<(i32, TypeId), Rc<dyn MyTrait>>
}

impl MyStruct {
    fn get<T: MyTrait + 'static>(&mut self, key: i32) -> Rc<T> {
        if let Some(item) = self.items.get(&(key, TypeId::of::<T>())) {
            item.clone() as Rc<T> // !!! Compilation error !!!
        } else {
            let item = Rc::new(T::new());
            self.items.insert((key, TypeId::of::<T>()), item.clone());
            item
        }
    }
}
kiv_apple
  • 491
  • 3
  • 13

1 Answers1

0

Rust is complaining because, on the current stable compiler, you simply can't cast between traits with as. You can however cast an Rc<dyn Any> to Rc<T> with downcast(), but it needs to be Rc<dyn Any>, not Rc<dyn MyTrait>, and it returns a Result because (as far as the compiler knows) it might fail. For example:

struct MyStruct {
    items: HashMap<(i32, TypeId), Rc<dyn Any>>
}

impl MyStruct {
    fn get<T: MyTrait + 'static>(&mut self, key: i32) -> Rc<T> {
        if let Some(item) = self.items.get(&(key, TypeId::of::<T>())) {
            item.clone().downcast::<T>().unwrap()
        } else {
            // ...

The trait_upcasting feature on the current nightly compiler allows you to store your values as Rc<dyn MyTrait> and upcast to Rc<dyn Any> with as later:

#![feature(trait_upcasting)]

struct MyStruct {
    items: HashMap<(i32, TypeId), Rc<dyn MyTrait>>
}

impl MyStruct {
    fn get<T: MyTrait + 'static>(&mut self, key: i32) -> Rc<T> {
        if let Some(item) = self.items.get(&(key, TypeId::of::<T>())) {
            (item.clone() as Rc<dyn Any>).downcast::<T>().unwrap()
        } else {
            // ...
Dan Getz
  • 8,774
  • 6
  • 30
  • 64
  • Is it possible somehow save information that in the map we store not any object, but only implementations of MyTrait, so I can call directly methods of MyTrait when I don't need exact struct? – kiv_apple Oct 08 '22 at 18:57
  • @kiv_apple turns out there's a new feature on nightly that would let you do that, I'll edit – Dan Getz Oct 08 '22 at 20:07