I want to implement a class Storage
that can store objects of any types. I am trying to do that using trait Any
. The Storage::insert::<T>(key, value)
should add a pair, where the key is always some String
type, and the value can be any type. When I store a Box<HashMap<String, dyn Any>>
the compiler says that it doesn't have size at compile-time. So how can I avoid that error ?
use std::any::{Any, TypeId};
use std::collections::hash_map::Keys;
use std::collections::HashMap;
pub struct Storage where Self: Sized{
map: Box<HashMap<String, dyn Any>>,
}
impl Storage {
pub fn new() -> Self {
Self {
map: Some(Box::new(HashMap::new())),
}
}
pub fn insert<Q: Any>(&mut self, key: &dyn Any, obj: Q) {
if key.is::<String>() {
let key_string = key.downcast_ref::<String>().unwrap();
self.map.as_mut().insert(key_string.clone(), obj);
}
}
}
Also I'm not sure that such class can be implemented with std::collections::HashMap
The problem is that HaspMap<K, V>
needs to have Sized
types K
and V
, which is not the case of dyn Any
. This means that the size of K
and V
must be known at compile time. For instance, you always know that an u32
will take 32 bits, but the size of [u32]
is not known beforehand. Therefore: u32: Sized
, and [u32]: !Sized
.
This is also the case of dyn Any
, dyn Any: !Sized
, because objects of different size can implement Any
, and, in fact, every object implements Any
.
To solve this, there is an easy patch, and it's wrapping that type with Box<_>
. Box
is a pointer to a heap-allocated memory, so you know its size will always be the size of a pointer (roughly speaking), and you don't need to know at compile time how much memory you will allocate on the heap. Here it goes:
pub struct Storage {
map: Box<HashMap<String, Box<dyn Any>>>,
}
You will also have to adapt the rest of the code, but you can find everything in the documentation.