【问题标题】:Hash trait does not work for Rc<RefCell<T>> in enum哈希特征不适用于枚举中的 Rc<RefCell<T>>
【发布时间】:2021-10-13 15:01:00
【问题描述】:

我定义了一个结构 MyData 并为它手动实现 PartialEqHash 特征。

我定义了一个枚举,其中包括Rc&lt;MyData&gt;Rc&lt;RefCell&lt;MyData&gt;&gt;

我想为枚举派生PartialEqHash,但失败了:

  1. PartialEqHash 都适用于 Rc&lt;MyData&gt;
  2. PartialEq 也适用于 Rc&lt;RefCell&lt;MyData&gt;&gt;
  3. Hash 不适用于Rc&lt;RefCell&lt;MyData&gt;&gt;

我有两个问题:

  1. 为什么?为什么只有Hash 不适用于Rc&lt;RefCell&lt;MyData&gt;&gt;

  2. 如何解决?

    我无法为Rc&lt;RefCell&lt;MyData&gt;&gt; 实现Hash。在四处寻找之后,我找到了一种方法:定义一个新的包装结构,比如struct RRWrapper&lt;T&gt; (Rc&lt;RefCell&lt;T&gt;&gt;),然后为此RRWrapper实现Hash。但这会带来很多代码。有没有惯用的方法?我认为这是一般用法。

提前致谢,

PS:在我的程序的真实代码中,枚举中只有Rc&lt;RefCell&lt;MyData&gt;&gt;,但没有Rc&lt;MyData&gt;。我把Rc&lt;MyData&gt;放在这里只是为了比较。 PS2:在我程序的真实代码中,枚举中有不止一个Rc&lt;RefCell&lt;T&gt;&gt;


原始源代码:

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() {
}

错误:

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)

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)))
    }
}

【问题讨论】:

  • 您正在努力为您的枚举派生 Hash,但手动实现它应该没有问题。但正如@Netwave 所说,使具有内部可变性的对象可散列不一定是一个好主意。

标签: rust hash enums traits refcell


【解决方案1】:

为什么?为什么只有 Hash 不适用于 Rc

如果你仔细想想,Hash 没有为RefCell 实现是有道理的。既然它是对内部可变性的抽象,那么可能会改变的东西的哈希值是什么?一般来说,可变的东西不适合作为Hash对象工作。

如何解决?

和你一样。对你真正想要的负责。为包装器实现它。

【讨论】:

  • String 也是可变的,它有Hash。同样,我认为Rc&lt;RefCell&lt;String&gt;&gt; 拥有Hash 是有意义的。
  • 我明白你的意思:RefCell 没有实现 Hash。谢谢。
  • let s: String 是不可变的,let mut s: String 是。然而let r: RefCell&lt;…&gt; 是可变的,即使它不使用let mut
猜你喜欢
  • 2019-12-13
  • 2023-01-20
  • 1970-01-01
  • 1970-01-01
  • 2020-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多