【问题标题】:How to implement the std::hash::Hash trait on external data types in Rust? [duplicate]如何在 Rust 中实现外部数据类型的 std::hash::Hash 特征? [复制]
【发布时间】:2019-05-01 07:26:18
【问题描述】:

我有一个基本的 LinkedList 实现,我想在其中迭代我的节点,并将这些节点添加到 HashSet。但是,我无法执行此操作,因为我的节点被包裹在 Rc<RefCell<Node<T>>> 中,并且我无法为我的 std::cell::Ref<'_, Node<T>> 类型实现 std::hash::Hash 特征。

我怎样才能在这个例子中实现Hash trait?还是我错过了什么?

这是一个尝试将一些节点添加到HashSet 的失败测试用例的示例:

这里是 Rust 操场上的源代码: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d79329dcb70ba54ff803dbcd93bd47d0

这里是来源:

use std::cell::{RefCell, Ref};
use std::fmt;
use std::fmt::Display;
use std::ptr;
use std::rc::Rc;
use std::hash::{Hash, Hasher};
use std::collections::HashSet;

pub type NodeRef<T> = Rc<RefCell<Node<T>>>;

#[derive(Debug)]
pub struct LinkedList<T> {
    pub head: Option<NodeRef<T>>,
}

// #[derive(Hash)]
pub struct Node<T> {
    pub data: T,
    pub next: Option<NodeRef<T>>,
}

impl<T: fmt::Debug> fmt::Debug for Node<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Node {{ data: {:?}, next: {:?} }}", self.data, self.next)
    }
}

impl<T> LinkedList<T>
where
    T: std::cmp::Eq
        + std::hash::Hash
        + std::clone::Clone
        + std::cmp::PartialOrd
        + std::cmp::PartialOrd
        + std::fmt::Debug,
{
    pub fn new() -> Self {
        Self { head: None }
    }

    pub fn append(&mut self, new_value: T) {
        if let Some(tail) = self.tail() {
            tail.borrow_mut().next = Some(Rc::new(RefCell::new(Node {
                data: new_value,
                next: None,
            })));
        } else {
            self.head = Some(Rc::new(RefCell::new(Node {
                data: new_value,
                next: None,
            })));
        }
    }

    pub fn tail(&self) -> Option<NodeRef<T>> {
        for node in self.iter() {
            if let None = node.clone().borrow().next {
                return Some(node);
            }
        }
        None
    }

    pub fn iter(&self) -> Iter<T> {
        Iter {
            next: self.head.clone(),
        }
    }
}

#[derive(Debug)]
pub struct Iter<T> {
    next: Option<NodeRef<T>>,
}

impl<'a, T> Iterator for Iter<T> {
    type Item = NodeRef<T>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(next) = self.next.clone() {
            // Set the new self.next:
            if let Some(new_next) = next.borrow().next.clone() {
                self.next = Some(new_next);
            } else {
                self.next = None;
            }

            return Some(next);
        } else {
            None
        }
    }
}

impl<T: Display> Display for LinkedList<T> {
    fn fmt(&self, w: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
        write!(w, "[")?;
        let mut node = self.head.clone();
        while let Some(n) = node {
            write!(w, "{}", n.borrow().data)?;
            node = n.borrow().next.clone();
            if node.is_some() {
                write!(w, ", ")?;
            }
        }
        write!(w, "]")
    }
}

impl<T: PartialEq> PartialEq for Node<T> {
    fn eq(&self, other: &Self) -> bool {
        if ptr::eq(self, other) {
            // For loop detection - if the nodes share the same
            // reference, they are equal.
            return true;
        }
        self.data == other.data && self.next == other.next
    }

    fn ne(&self, other: &Self) -> bool {
        if !ptr::eq(self, other) {
            return true;
        }
        self.data != other.data && self.next == other.next
    }
}

// By implementing Eq, we are making the promise that our
// implementation of PartialEq is reflexive.
impl<T: Eq> Eq for Node<T> {
}


impl<T: Hash> std::hash::Hash for Node<T> {
    fn hash<H>(&self, state: &mut H)
        where H: std::marker::Sized + Hasher
    {
        self.data.hash(state);
        if let Some(next) = self.next.clone() {
            next.borrow().hash(state);
        }
    }
}

// // TODO: HELP!!!
// // Trying to implement Hash trait for Ref<'_, Node<T>>:
// impl<Node: Hash> std::hash::Hash for Ref<'_, Node> {
//     fn hash<H: Hasher>(&self, state: &mut H) {
//         self.data.hash(state);
//         if let Some(next) = self.next.clone() {
//             next.borrow().hash(state);
//         }
//     }
// }

impl<T: PartialEq> PartialEq for LinkedList<T> {
    fn eq(&self, other: &Self) -> bool {
        self.head == other.head
    }

    fn ne(&self, other: &Self) -> bool {
        self.head != other.head
    }
}

impl<T: Eq + std::fmt::Debug> Eq for LinkedList<T> {}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn eq() {
        let mut list = LinkedList::new();
        list.append(1);
        list.append(2);
        list.append(3);
        let mut list2 = LinkedList::new();
        list2.append(1);
        list2.append(2);
        list2.append(3);

        assert_eq!(list, list2);
        list2 = LinkedList::new();
        list2.append(3);
        assert_ne!(list, list2);
        list = LinkedList::new();
        list.append(3);
        assert_eq!(list, list2);
    }

    #[test]
    fn append() {
        let mut list = LinkedList::new();
        list.append(1);
        list.append(2);
        list.append(3);
        let mut list2 = LinkedList::new();
        list2.append(1);
        list2.append(2);
        list2.append(3);

        assert_eq!(list, list2);
        list2.append(1);
        assert_ne!(list, list2);
        list.append(1);
        assert_eq!(list, list2);
    }

    // TODO: fix this test case!
    #[test]
    fn hashset_iter_nodes() {
        let mut list = LinkedList::new();
        list.append(1);
        list.append(2);
        list.append(3);

        // the trait bound `std::cell::Ref<'_, Node<{integer}>>:
        // std::hash::Hash` is not satisfied (the trait `std::hash::Hash` is
        // not implemented for `std::cell::Ref<'_, Node<{integer}>>`)
        // [E0277]
        // the trait bound `std::cell::Ref<'_, Node<{integer}>>:
        // std::cmp::Eq` is not satisfied (the trait `std::cmp::Eq` is
        // not implemented for `std::cell::Ref<'_, Node<{integer}>>`)
        // [E0277]
        let mut set = HashSet::new();
        // iterate over nodes, adding each node to our hashset:
        for node in list.iter() {
            println!("iterating over node: {:?}", node);
            set.insert(&node.borrow());
        }
        assert_eq!(set.len(), 3);
    }
}

【问题讨论】:

  • 我不认为HashSet&lt;Ref&lt;_&gt;&gt; 是一个好主意,因为Ref 只是&amp;T 的包装,用作borrow() 的返回。为什么不做HashSet&lt;NodeRef&lt;T&gt;&gt;?毕竟这是一个Rc,所以你可以做set.insert(node.clone())(它也失败了,但看起来更容易修复)。
  • node.clone() 返回一个RefCell&lt;Node&lt;T&gt;&gt;,所以我遇到了同样的问题,Hash 特征没有为RefCell&lt;Node&lt;T&gt;&gt; 实现,并且我不允许实现trait 因为RefCell 没有在我的模块中定义。也许我应该找到另一种方法来将我的 NodeRef 类型 - 这是一个 Rc - 添加到我的 HashSet?
  • 其实node是一个NodeRef,也就是Rc&lt;RefCell&lt;Node&lt;T&gt;&gt;&gt;的别名。你不能为此实现Hash,但是,看看this possible duplicate
  • @rodrigo 非常感谢您的提示!这使我找到了解决方案,其中涉及定义一个包装类型,我可以为其实现 Hash 特征。我在下面发布了解决方案。非常感谢您的建议????

标签: rust hashset refcell


【解决方案1】:

为了将现有类型添加到 HashSet,我按照上面 cmets 中的@rodrigo's advice,并定义了一个新的包装类型:

pub struct HashedNode<T>(Rc<RefCell<Node<T>>>);

这样做让我可以在我的 Rc&lt;RefCell&lt;Node&lt;T&gt;&gt;&gt; 类型上实现 std::hash::Hash 特征,如下所示:

impl<T: Hash> std::hash::Hash for HashedNode<T> {
  fn hash<H: Hasher>(&self, state: &mut H) {
     // ... etc
  }
}

这让我可以定义一个 HashSet 并根据需要使用它:

let mut set: HashSet<HashedNode<i32>> = HashSet::new();

请注意,还需要一些其他解决方法,例如为我的新 HashedNode 类型实现 Eq 特征,以及实现 From 特征和 from_node 实用方法。

这是上例中完成的代码:

use std::cell::{RefCell, Ref};
use std::fmt;
use std::fmt::Display;
use std::ptr;
use std::rc::Rc;
use std::hash::{Hash, Hasher};
use std::collections::HashSet;

pub type NodeRef<T> = Rc<RefCell<Node<T>>>;

// Used specifically for hashing needs, like HashSet:
pub struct HashedNode<T>(NodeRef<T>);

#[derive(Debug)]
pub struct LinkedList<T> {
    pub head: Option<NodeRef<T>>,
}

// #[derive(Hash)]
pub struct Node<T> {
    pub data: T,
    pub next: Option<NodeRef<T>>,
}

impl<T: fmt::Debug> fmt::Debug for Node<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Node {{ data: {:?}, next: {:?} }}", self.data, self.next)
    }
}

impl<T> LinkedList<T>
where
    T: std::cmp::Eq
        + std::hash::Hash
        + std::clone::Clone
        + std::cmp::PartialOrd
        + std::cmp::PartialOrd
        + std::fmt::Debug,
{
    pub fn new() -> Self {
        Self { head: None }
    }

    pub fn append(&mut self, new_value: T) {
        if let Some(tail) = self.tail() {
            tail.borrow_mut().next = Some(Rc::new(RefCell::new(Node {
                data: new_value,
                next: None,
            })));
        } else {
            self.head = Some(Rc::new(RefCell::new(Node {
                data: new_value,
                next: None,
            })));
        }
    }

    pub fn tail(&self) -> Option<NodeRef<T>> {
        for node in self.iter() {
            if let None = node.clone().borrow().next {
                return Some(node);
            }
        }
        None
    }

    pub fn iter(&self) -> Iter<T> {
        Iter {
            next: self.head.clone(),
        }
    }
}

#[derive(Debug)]
pub struct Iter<T> {
    next: Option<NodeRef<T>>,
}

impl<'a, T> Iterator for Iter<T> {
    type Item = NodeRef<T>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(next) = self.next.clone() {
            // Set the new self.next:
            if let Some(new_next) = next.borrow().next.clone() {
                self.next = Some(new_next);
            } else {
                self.next = None;
            }

            return Some(next);
        } else {
            None
        }
    }
}

impl<T: Display> Display for LinkedList<T> {
    fn fmt(&self, w: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
        write!(w, "[")?;
        let mut node = self.head.clone();
        while let Some(n) = node {
            write!(w, "{}", n.borrow().data)?;
            node = n.borrow().next.clone();
            if node.is_some() {
                write!(w, ", ")?;
            }
        }
        write!(w, "]")
    }
}

impl<T: PartialEq> PartialEq for Node<T> {
    fn eq(&self, other: &Self) -> bool {
        if ptr::eq(self, other) {
            // For loop detection - if the nodes share the same
            // reference, they are equal.
            return true;
        }
        self.data == other.data && self.next == other.next
    }

    fn ne(&self, other: &Self) -> bool {
        if !ptr::eq(self, other) {
            return true;
        }
        self.data != other.data && self.next == other.next
    }
}

// By implementing Eq, we are making the promise that our
// implementation of PartialEq is reflexive.
impl<T: Eq> Eq for Node<T> {
}


impl<T: Hash> std::hash::Hash for HashedNode<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        // TODO: make hash work for nodes that have the same data
        self.0.borrow().data.hash(state);
    }
}

impl<T> From<T> for HashedNode<T> {
    fn from(item: T) -> Self {
        HashedNode(Rc::new(RefCell::new(Node {
            data: item,
            next: None,
        })))
    }
}

impl<T> HashedNode<T> {
    pub fn from_node(node: NodeRef<T>) -> Self {
        HashedNode(node)
    }
}


impl<T: PartialEq> PartialEq for HashedNode<T> {
    fn eq(&self, other: &Self) -> bool {
        if ptr::eq(self, other) {
            // For loop detection - if the nodes share the same
            // reference, they are equal.
            return true;
        }
        let other_node = other.0.borrow();
        let self_node = self.0.borrow();
        self_node.data == other_node.data && self_node.next == other_node.next
    }
}

impl<T: Eq> Eq for HashedNode<T> {}

impl<T: PartialEq> PartialEq for LinkedList<T> {
    fn eq(&self, other: &Self) -> bool {
        self.head == other.head
    }

    fn ne(&self, other: &Self) -> bool {
        self.head != other.head
    }
}

impl<T: Eq + std::fmt::Debug> Eq for LinkedList<T> {}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn eq() {
        let mut list = LinkedList::new();
        list.append(1);
        list.append(2);
        list.append(3);
        let mut list2 = LinkedList::new();
        list2.append(1);
        list2.append(2);
        list2.append(3);

        assert_eq!(list, list2);
        list2 = LinkedList::new();
        list2.append(3);
        assert_ne!(list, list2);
        list = LinkedList::new();
        list.append(3);
        assert_eq!(list, list2);
    }

    #[test]
    fn append() {
        let mut list = LinkedList::new();
        list.append(1);
        list.append(2);
        list.append(3);
        let mut list2 = LinkedList::new();
        list2.append(1);
        list2.append(2);
        list2.append(3);

        assert_eq!(list, list2);
        list2.append(1);
        assert_ne!(list, list2);
        list.append(1);
        assert_eq!(list, list2);
    }

    #[test]
    // Tests that our nodes can be hashed and saved into a set.
    fn hashset_iter_nodes() {
        let node = Rc::new(RefCell::new(Node{ data: 9, next: None}));
        let mut list = LinkedList::new();
        list.append(1);
        list.append(2);
        list.append(3);
        list.append_node(node.clone());

        let mut set: HashSet<HashedNode<i32>> = HashSet::new();
        // iterate over nodes, adding each node to our hashset:
        for node in list.iter() {
            set.insert(HashedNode::from_node(node));
        }
        assert_eq!(set.contains(&HashedNode::from_node(node)), true);
        assert_eq!(set.contains(&HashedNode::from(4)), false);
        assert_eq!(set.len(), 4);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-10-26
    • 1970-01-01
    • 1970-01-01
    • 2020-08-04
    • 2014-06-13
    • 2021-06-07
    • 1970-01-01
    • 2022-08-15
    相关资源
    最近更新 更多