0

I have different structs which I am storing in a HashMap. These structs are sharing similar methods, but have also their own specific methods. I want to store all the different structs in a HashMap, and be able to access both the trait methods and the specific methods belonging to each struct.

As of now, I've found a great way of using the trait methods of structs stored in a HashMap, but my problem is now to access the methods belonging to each specific struct.

This is a very simplified structure of my code:

struct A {value_only_A_has: f64, common_value: u32}

impl A {
    fn method_a() {
        // Does something very specific and only related to A...
    }
}

impl Common for A {
    fn method_common(&mut self) -> () {
        // Does something which is related to all structs, but properties are only 
        // related to A (the reason for using traits).
    }
}

struct B {value_only_B_has: f64, common_value: u32}

impl B {
    fn method_b() {
        // Does something very specific and only related to B...
    }
}

impl Common for B {
    fn method_common(&mut self) -> () {
        // Does something which is related to all structs, but properties are only 
        // related to B (the reason for using traits).
    }
}

trait Common {
    // Does something which is related to all structs.
    fn method_common(&mut self) -> ();
}

This is a very simplified explanation of what I want to do:

// Store all different kind of structures in a HashMap container.
let mut container: HashMap<u32, Box<dyn Common>> = HashMap::new();
let a = A {value_only_A_has: 10.0, common_value: 3};
let b = B {value_only_B_has: 20.0, common_value: 2};
container.insert(0, Box::new(a));
container.insert(1, Box::new(b));

// This trait method is easy to access (this works):
container.get(&1).unwrap().method_common();

// I want to somehow get access to the method
// belonging to each struct (this does not work):
match container.get(&1).unwrap() {
    A(v) => v.method_a(),
    B(v) => v.method_b(),
}

How can I access the methods specific to each struct by using something ideally similar to this?

(Edit: After playing around with Box, it seems that the only thing stored in container are the trait methods, and not the actual struct with the specific methods. Maybe my approach by storing the structs in container as trait and Box in the way I've done it is wrong?)

Ólavur Nón
  • 161
  • 1
  • 13
  • If you don't want an enum, this is the correct approach. You just need to add downcasting abilities to your trait. – Chayim Friedman Aug 12 '22 at 10:55
  • You might still be better served with a hashmap of enums. Please update the question to refer whether this resolves your use case or not. Relevant: https://stackoverflow.com/questions/27957103/how-do-i-create-a-heterogeneous-collection-of-objects – E_net4 Aug 12 '22 at 11:01
  • @ChayimFriedman, if you'll like, i would love to see an answer to my problem using the same approach in your attached link. I took a look at the link, but it seems to only be for one struct, and not for my case, a varying amount of structs all implementing 'Common'. – Ólavur Nón Aug 12 '22 at 11:21
  • @E_net4thecommentflagger, if a variation of the answer in Chayim Friedmans attached link is found, then that will probably be the solution, otherwise, i'll use enums. – Ólavur Nón Aug 12 '22 at 11:23
  • @ChayimFriedman, i'm sorry, i realize that the answer given in your attachment is exactly what i need. I misunderstood some things when i asked about a varying amount of structs all implementing 'Common'. I ofcourse only need to get one specific struct from one 'match', as i'll in my case always know what i put into every match used and what i'll get from that same 'match'. – Ólavur Nón Aug 12 '22 at 12:38

1 Answers1

0

A possible approach is to use enum for value types:

use std::collections::HashMap;

enum CommonValue {
    A,
    B,
}
...

fn main() {
    let mut hello: HashMap<u32, CommonValue> = HashMap::new();

    ...

    match hello.get(&1).unwrap() {
        CommonValue::A(v) => v.method_a(),
        CommonValue::B(v) => v.method_b(),
    }
}

Also, if you want to run .method_common on CommonValue, you can implement Common trait for the enum CommonValue:

impl Common for CommonValue {
    fn method_common(&mut self) -> () {
        // ...
    }
}

...

hello.get_mut(&1).unwrap().method_common();
user1635327
  • 1,469
  • 3
  • 11
  • Using this method, i'll have to include 'match' also for the trait method 'method_common', right? This i find not desirable, but surely it is a solution to the problem. – Ólavur Nón Aug 12 '22 at 11:17
  • It can be done by implementing `Common` trait for `CommonValue`. I've added details to the answer. – user1635327 Aug 12 '22 at 12:50