Which works great, but I'm having trouble creating a HashMap with different types T inside MappedStore. I tried enum variation but I can't specify Message + Default + Clone and it wont satisfy the compiler. I believe a trait object would be the way to go but with Default and Clone rust won't be able to create it.
the trait `StorableMessage` cannot be made into an object
`StorableMessage` cannot be made into an object
the size for values of type `(dyn StorableMessage + 'static)` cannot be known at compilation time
the trait `Sized` is not implemented for `(dyn StorableMessage + 'static)`
the trait `Message` is implemented for `Box<M>`
required because of the requirements on the impl of `Message` for `Box<(dyn StorableMessage + 'static)>`
Clone is doable if you use an enum (not dyn) and #[derive(Clone)] on that enum.
For Default, you'll need to decide: what is the default value? You'll have to pick one of the types, or have an enum variant for being the "default/unspecified/nothing" value. Perhaps you don't actually need Default, though — after all, the HashMap can simply not contain a value for that key.
If you want more specific advice about your problem, you'll need to tell us more of what the problem is — what kinds of different types do you want to store, and for what purpose?
Box is a "fundamental" type, which means that you can implement traits for Box<LocalType> as if you were implementing for LocalType. In particular here, you can implement Default and Clone for Box<dyn StorableMessage>, even though the type and traits are both foreign.
For the former you'd have to choose a suitable base type. For the latter you'll need a little further help to bridge the dyn type erasure, e.g.:
I'm creating GRPC services for a project, those are using Protobuf messages. I also need to store some configuration data on server and well since I already have nice binary serialization in the protobuf message itself then thats what I use. MappedStore manages a collection of multiple indexed protobuf messages. For the store to function I had (compiler required it) to add traits such as Message + Default + Clone so it could encode and decode a generic protobuf message.
I have that working and now I'm trying to create a HashMap where I can access multiple mapped stores for a GRPC service. In the future I would store just references to the stores, but I decided to first figure out how to get multiple MappedStore<MessageA> and MappedStore<MessageB> into the same HashMap while both MessageA and MessageB are valid protobuf messages.
My problem is I cant find the solution to a HashMap that allows multiple types of protobuf messages which would satisfy traits of my MappedStore, rather I don't even know if such a multiple type which satisfies multiple traits can be done
So I wondered why I specify the same traits on my struct and impl and realized I only need Message trait on my struct with allowed me to simplify my HashMap type to just the the Message trait. However the compiler errors out with it being unsized at compilation time. But how am I supposed to do so when different messages have different sizes? I thought the Box type would prevent something like this when it allocates to the heap.
At a guess, something on Message implies Sized, but that's just a guess. What's the full error (from cargo check, say), and the declaration of Message?
The Sized is probably generated for each message struct from protoc.
Cargo check:
error[E0277]: the size for values of type `(dyn Message + 'static)` cannot be known at compilation time
--> src/services/store.rs:12:13
|
12 | stores: HashMap<String, Box<MappedStore<dyn Message>>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn Message + 'static)`
note: required by a bound in `store::MappedStore`
--> src/store.rs:18:24
|
18 | pub struct MappedStore<T: Message> {
| ^ required by this bound in `store::MappedStore`
help: consider relaxing the implicit `Sized` restriction
|
18 | pub struct MappedStore<T: Message + ?Sized> {
| ++++++++
error[E0277]: the size for values of type `(dyn Message + 'static)` cannot be known at compilation time
--> src/services/store.rs:22:40
|
22 | pub fn add_store(&mut self, store: Box<MappedStore<dyn Message>>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn Message + 'static)`
note: required by a bound in `store::MappedStore`
--> src/store.rs:18:24
|
18 | pub struct MappedStore<T: Message> {
| ^ required by this bound in `store::MappedStore`
help: consider relaxing the implicit `Sized` restriction
|
18 | pub struct MappedStore<T: Message + ?Sized> {
| ++++++++
Thanks for pointing that out, I was trying things out by rearranging the Box type. Still results in a very similar/same error.
error[E0277]: the size for values of type `(dyn Message + 'static)` cannot be known at compilation time
--> src/services/store.rs:12:13
|
12 | stores: HashMap<String, MappedStore<Box<dyn Message>>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn Message + 'static)`
= help: the trait `Message` is implemented for `Box<M>`
= note: required because of the requirements on the impl of `Message` for `Box<(dyn Message + 'static)>`
note: required by a bound in `store::MappedStore`
--> src/store.rs:18:27
|
18 | pub struct MappedStore<T: Message> {
| ^^^^^^^ required by this bound in `store::MappedStore`
You might be able to create your own subtrait that meets your use-case (ala the example linked in that last comment?), but that's where I stopped digging.
I think I made some progress, I changed MappedStore to just pub struct MappedStore<T> {} without any traits for T and I have a compiling HashMap of MappedStore<dyn Message>.
But now i wonder how do I add stores to the map when they used a generic and not a Box. I can wrap my types in a Box but there is still something wrong that Im not grasping.
Instancing code:
Note: I wrapped CpuTempResponse in a Box only to try out if I even need to
let cpu_store = MappedStore::<Box<CpuTempResponse>>::new(env::current_dir().unwrap(), None);
let gpu_store = MappedStore::<GpuTempResponse>::new(env::current_dir().unwrap(), None);
let storeTable = MappedStoreTable::new();
storeTable.add_store(&cpu_store);
storeTable.add_store(&gpu_store);