如何在 Rust 的外部数据类型上实现 std::hash::Hash 特征?
How to implement the std::hash::Hash trait on external data types in Rust?
我有一个基本的 LinkedList 实现,我想在其中循环访问我的节点,并将这些节点添加到 HashSet。但是,我无法执行此操作,因为我的节点被包裹在 Rc<RefCell<Node<T>>>
中,而且我在为我的 std::cell::Ref<'_, Node<T>>
类型实现 std::hash::Hash
特性时遇到了问题。
如何为这个例子实现 Hash
特性?还是我遗漏了什么?
这是一个失败的测试用例示例,它试图将一些节点添加到 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,我按照上面评论中的 定义了一个新的包装类型:
pub struct HashedNode<T>(Rc<RefCell<Node<T>>>);
这样做让我可以在我的 Rc<RefCell<Node<T>>>
类型上实现 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);
}
}
我有一个基本的 LinkedList 实现,我想在其中循环访问我的节点,并将这些节点添加到 HashSet。但是,我无法执行此操作,因为我的节点被包裹在 Rc<RefCell<Node<T>>>
中,而且我在为我的 std::cell::Ref<'_, Node<T>>
类型实现 std::hash::Hash
特性时遇到了问题。
如何为这个例子实现 Hash
特性?还是我遗漏了什么?
这是一个失败的测试用例示例,它试图将一些节点添加到 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,我按照上面评论中的
pub struct HashedNode<T>(Rc<RefCell<Node<T>>>);
这样做让我可以在我的 Rc<RefCell<Node<T>>>
类型上实现 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);
}
}