How can I make a ` Vec<dyn C> ` to be a ` HashMap ` key?

There is the code:

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
struct A;
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
struct B;

trait C {}

struct D {
    d: Vec<Box<dyn C>>
}

impl C for A {}
impl C for B {}

let dv: Vec<Box<dyn C>> = vec![Box::new(A), Box::new(B)];
let d = D { d: dv };

let h: HashMap<D, i32> = HashMap::new();
h.insert(d, 1);

This question is easier to answer if you provide a more realistic use-case context, since I can think of multiple approaches how implementing Eq and Hash for a struct like your D struct) could become feasible.


Make sure in particular to answer:

Are Ds actually supposed to be considered equal depending on their contents? Using a Vec key for a hashmap is possible, but not the most common thing. Especially also note that the order of the elements will matter. Is a hashmap even what you want here? What's the use-case, roughly?

Comparing the Ds by their vec elements, are elements of distinct type always supposed to be unequal, but values of same type compared by value? I. e. there isn't some kind of "object" identity in use here that you could simply expose in the trait and use instead.

If all of this is actually what you want, then using something like what this crate offers / helps with – https://crates.io/crates/dyn_partial_eq – could work for you. (I'm not familiar with the crate beyond glancing at their basic API and idea, and beyond copying the idea, you might need to roll your own solution to create an appropriate Eq and also Hash impl, or look for other comparable crates that already have hashing in mind.)

2 Likes

There is a more realistic situation I am stucking. The following persu-do code maybe can not compile, but logic is brief: I am doing some heavy calculation, the calculation argument is a struct Dcon with field of Vec<dyn Convert>. And there is a HashMap<Dcon, calculated result> named data_save. Every time I get the result of calculating of Dcon, I put it in the data_save as key is the Dcon, value is the result. So next time when I need to calculate the same Dcon, I can get it directly from data_save rather than calculating it again.

trait Convert {
    fn convert(&self, i: &Vec<i32>) -> Vec<i32>;
}

struct Con1;
struct Con2;

impl Convert for Con2 {
    fn convert(&self, i: &Vec<i32>) -> Vec<i32> {
        // in real situation, this is some block of consuming much time to calculation
        i.iter().map(|x| x + 1).collect()
    }
}

impl Convert for Con2 {
    fn convert(&self, i: &Vec<i32>) -> Vec<i32> {
        // in real situation, this is some block of consuming much time to calculation
        i.iter().map(|x| x + 2).collect()
    }
}


struct A {
    data_save: HashMap<Dcon ,Vec<i32>>
}

struct Dcon {
    dcon: Vec<dyn Convert>
}

impl A {
    fn save_convert(&self, dcon: Dcon, i: Vec<i32>) -> &Vec<i32> {
        if self.data_save.contains_key(&dcon) {
            return &self.data_save[&dcon]
        }
        let mut res = dcon.dcon[0].convert(&i);
        for i = 1..dcon.dcon.len() {
            res = dcon.dcon[i].convert(&res)
        }
        self.data_save.insert(dcon, res);
        return &self.data_save[&dcon]
    }
}

I tried in this way , but something wrong:

use std::hash::{Hash, Hasher};
use std::collections::HashMap;

trait Foo {
    fn id(&self) -> Vec<i64>;
}
struct A(i64, i64);
struct B(i64);
impl Foo for A {
    fn id(&self) -> Vec<i64> { vec![1i64, self.0, self.1] }
}
impl Foo for B {
    fn id(&self) -> Vec<i64> { vec![2i64, self.0] }
}

impl Hash for Box<dyn Foo> {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        self.hash(state)
    }
}

impl PartialEq for Box<dyn Foo> {
    fn eq(&self, other: &Box<dyn Foo>) -> bool {
        self.id() == other.id()
    }
}

impl Eq for Box<dyn Foo> {}

fn main() {
    let a: Box<dyn Foo> = Box::new(A(1, 2));
    let b: Box<dyn Foo> = Box::new(B(1));
    let foo_vec = vec![a, b];
    let mut h = HashMap::new();
    h.insert(foo_vec, 1);
}


thread 'main' has overflowed its stack
error: process didn't exit successfully: `target\debug\bt.exe` (exit code: 0xc00000fd, STATUS_STACK_OVERFLOW)