rusthashenumstraitsrefcell

Hash trait does not work for Rc<RefCell<T>> in enum


I define a struct MyData and implement PartialEq and Hash traits for it manually.

I define a enum which includes Rc<MyData> and Rc<RefCell<MyData>>.

I want derive PartialEq and Hash for the enum, but fails:

  1. The PartialEq and Hash both work for Rc<MyData>;
  2. The PartialEq works for Rc<RefCell<MyData>> too;
  3. But the Hash does not work for Rc<RefCell<MyData>> !

I have 2 questions:

  1. Why? Why only the Hash does not work only for Rc<RefCell<MyData>>?

  2. How to fix it?

    I can not implement Hash for Rc<RefCell<MyData>>. After searching around I find a way: defining a new wrapper struct, like struct RRWrapper<T> (Rc<RefCell<T>>), and then implement Hash for this RRWrapper. But this will bring much code. Is there an idiomatic way? I think this's a general usage.

Thank in advance,

Wu

PS: In the real code of my program, there is only Rc<RefCell<MyData>> in the enum but no Rc<MyData>. I put Rc<MyData> here just for comparision. PS2: In the real code of my program, there are more than one Rc<RefCell<T>> in the enum.


Original source code:

use std::rc::Rc;
use std::cell::RefCell;
use std::hash::{Hash, Hasher};

struct MyData {
    i: i64,
}

impl Hash for MyData {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.hash(state);
    }
}
impl PartialEq for MyData {
    fn eq(&self, other: &Self) -> bool {
        self == other
    }
}

#[derive(PartialEq, Hash)]
enum MyEnum {
    INT(i64),
    STR(String),
    MYDATA1(Rc<MyData>), // OK both
    MYDATA2(Rc<RefCell<MyData>>), // OK for PartialEq but not for Hash
}

fn main() {
}

Error:

20  | #[derive(PartialEq, Hash)]
    |                     ---- in this derive macro expansion
...
25  |     MYDATA2(Rc<RefCell<MyData>>), // OK for PartialEq but not for Hash
    |             ^^^^^^^^^^^^^^^^^^^ the trait `Hash` is not implemented for `RefCell<MyData>`
    |
    = note: required because of the requirements on the impl of `Hash` for `Rc<RefCell<MyData>>`
    = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info)

Source code of struct RRWrapper:


#[derive(Debug, PartialEq, Eq)]
pub struct RRWrapper<T: Hash+PartialEq+Eq>(Rc<RefCell<T>>);

impl<T: Hash+PartialEq+Eq> Deref for RRWrapper<T> {
    type Target = Rc<RefCell<T>>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
impl<T: Hash+PartialEq+Eq> Hash for RRWrapper<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.borrow().hash(state);
    }
}
impl<T: Hash+PartialEq+Eq> Clone for RRWrapper<T> {
    fn clone(&self) -> Self {
        RRWrapper(self.0.clone())
    }
}
impl<T: Hash+PartialEq+Eq> RRWrapper<T> {
    pub fn new(inner: T) -> Self {
        RRWrapper(Rc::new(RefCell::new(inner)))
    }
}

Solution

  • Why? Why only the Hash does not work only for Rc<RefCell>?

    If you think about it it makes sense that Hash is not implemented for RefCell. Since it is an abstraction for interior mutability, what could be the hash of something that may change? In general, mutable things are not suitable to work as Hash objects because of that.

    How to fix it?

    Exactly as you did. Taking responsibility in what you exactly want. Implementing it for a wrapper.