mirror of https://github.com/sunface/rust-course
Merge pull request #1349 from SUN-LG/main
✨ feat: add too many lists - 生产级双向 unsafe 队列
pull/1358/head
commit
434bcbf1e0
@ -1,10 +1,10 @@
|
||||
# 使用高级技巧实现链表
|
||||
|
||||
说句实话,我们之前实现的链表都达不到生产级可用的程度,而且也没有用到一些比较时髦的技巧。
|
||||
|
||||
本章我们一起来看一些更时髦的链表实现:
|
||||
|
||||
1. 生产级可用的双向链表
|
||||
2. 双重单向链表
|
||||
3. 栈分配的链表
|
||||
4. 自引用和Arena分配器实现( 原文作者还未实现,所以... Todo )
|
||||
5. GhostCell 实现( 同上 )
|
||||
1. 双重单向链表
|
||||
2. 栈分配的链表
|
||||
3. 自引用和Arena分配器实现( 原文作者还未实现,所以... Todo )
|
||||
4. GhostCell 实现( 同上 )
|
||||
|
@ -0,0 +1,572 @@
|
||||
# Filling In Random Bits
|
||||
|
||||
嘿,你不是说要做成精品吗?
|
||||
|
||||
为了成为一个 "好 "系列,这里还有一些乱七八糟的东西:
|
||||
|
||||
```rust
|
||||
impl<T> LinkedList<T> {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len == 0
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
// Oh look it's drop again
|
||||
while let Some(_) = self.pop_front() { }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
现在,我们已经有了一大堆大家都期待的特性需要实现:
|
||||
|
||||
```rust
|
||||
impl<T> Default for LinkedList<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for LinkedList<T> {
|
||||
fn clone(&self) -> Self {
|
||||
let mut new_list = Self::new();
|
||||
for item in self {
|
||||
new_list.push_back(item.clone());
|
||||
}
|
||||
new_list
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Extend<T> for LinkedList<T> {
|
||||
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
|
||||
for item in iter {
|
||||
self.push_back(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromIterator<T> for LinkedList<T> {
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.extend(iter);
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for LinkedList<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_list().entries(self).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq> PartialEq for LinkedList<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.len() == other.len() && self.iter().eq(other)
|
||||
}
|
||||
|
||||
fn ne(&self, other: &Self) -> bool {
|
||||
self.len() != other.len() || self.iter().ne(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq> Eq for LinkedList<T> { }
|
||||
|
||||
impl<T: PartialOrd> PartialOrd for LinkedList<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.iter().partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ord> Ord for LinkedList<T> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.iter().cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash> Hash for LinkedList<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.len().hash(state);
|
||||
for item in self {
|
||||
item.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
另一个有趣的话题是哈希本身。你看到我们如何将 `len` 写入散列的吗?这其实非常重要!如果集合不把 `len` 加入散列,很可能会意外的造成前缀碰撞。例如,一个集合包含 `["he", "llo"]` 另一个集合包含 `["hello"]`,我们该如何区分?如果没有把集合长度或其它"分隔符"加入到散列 ,这将毫无意义!会让意外哈希碰撞发生变得太容易,会导致严重的后果,所以还是照做吧!
|
||||
|
||||
好了,这是我们现在的代码:
|
||||
|
||||
```rust
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::{self, Debug};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::iter::FromIterator;
|
||||
use std::ptr::NonNull;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct LinkedList<T> {
|
||||
front: Link<T>,
|
||||
back: Link<T>,
|
||||
len: usize,
|
||||
_boo: PhantomData<T>,
|
||||
}
|
||||
|
||||
type Link<T> = Option<NonNull<Node<T>>>;
|
||||
|
||||
struct Node<T> {
|
||||
front: Link<T>,
|
||||
back: Link<T>,
|
||||
elem: T,
|
||||
}
|
||||
|
||||
pub struct Iter<'a, T> {
|
||||
front: Link<T>,
|
||||
back: Link<T>,
|
||||
len: usize,
|
||||
_boo: PhantomData<&'a T>,
|
||||
}
|
||||
|
||||
pub struct IterMut<'a, T> {
|
||||
front: Link<T>,
|
||||
back: Link<T>,
|
||||
len: usize,
|
||||
_boo: PhantomData<&'a mut T>,
|
||||
}
|
||||
|
||||
pub struct IntoIter<T> {
|
||||
list: LinkedList<T>,
|
||||
}
|
||||
|
||||
impl<T> LinkedList<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
front: None,
|
||||
back: None,
|
||||
len: 0,
|
||||
_boo: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_front(&mut self, elem: T) {
|
||||
// SAFETY: it's a linked-list, what do you want?
|
||||
unsafe {
|
||||
let new = NonNull::new_unchecked(Box::into_raw(Box::new(Node {
|
||||
front: None,
|
||||
back: None,
|
||||
elem,
|
||||
})));
|
||||
if let Some(old) = self.front {
|
||||
// Put the new front before the old one
|
||||
(*old.as_ptr()).front = Some(new);
|
||||
(*new.as_ptr()).back = Some(old);
|
||||
} else {
|
||||
// If there's no front, then we're the empty list and need
|
||||
// to set the back too.
|
||||
self.back = Some(new);
|
||||
}
|
||||
// These things always happen!
|
||||
self.front = Some(new);
|
||||
self.len += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_back(&mut self, elem: T) {
|
||||
// SAFETY: it's a linked-list, what do you want?
|
||||
unsafe {
|
||||
let new = NonNull::new_unchecked(Box::into_raw(Box::new(Node {
|
||||
back: None,
|
||||
front: None,
|
||||
elem,
|
||||
})));
|
||||
if let Some(old) = self.back {
|
||||
// Put the new back before the old one
|
||||
(*old.as_ptr()).back = Some(new);
|
||||
(*new.as_ptr()).front = Some(old);
|
||||
} else {
|
||||
// If there's no back, then we're the empty list and need
|
||||
// to set the front too.
|
||||
self.front = Some(new);
|
||||
}
|
||||
// These things always happen!
|
||||
self.back = Some(new);
|
||||
self.len += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop_front(&mut self) -> Option<T> {
|
||||
unsafe {
|
||||
// Only have to do stuff if there is a front node to pop.
|
||||
self.front.map(|node| {
|
||||
// Bring the Box back to life so we can move out its value and
|
||||
// Drop it (Box continues to magically understand this for us).
|
||||
let boxed_node = Box::from_raw(node.as_ptr());
|
||||
let result = boxed_node.elem;
|
||||
|
||||
// Make the next node into the new front.
|
||||
self.front = boxed_node.back;
|
||||
if let Some(new) = self.front {
|
||||
// Cleanup its reference to the removed node
|
||||
(*new.as_ptr()).front = None;
|
||||
} else {
|
||||
// If the front is now null, then this list is now empty!
|
||||
self.back = None;
|
||||
}
|
||||
|
||||
self.len -= 1;
|
||||
result
|
||||
// Box gets implicitly freed here, knows there is no T.
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop_back(&mut self) -> Option<T> {
|
||||
unsafe {
|
||||
// Only have to do stuff if there is a back node to pop.
|
||||
self.back.map(|node| {
|
||||
// Bring the Box front to life so we can move out its value and
|
||||
// Drop it (Box continues to magically understand this for us).
|
||||
let boxed_node = Box::from_raw(node.as_ptr());
|
||||
let result = boxed_node.elem;
|
||||
|
||||
// Make the next node into the new back.
|
||||
self.back = boxed_node.front;
|
||||
if let Some(new) = self.back {
|
||||
// Cleanup its reference to the removed node
|
||||
(*new.as_ptr()).back = None;
|
||||
} else {
|
||||
// If the back is now null, then this list is now empty!
|
||||
self.front = None;
|
||||
}
|
||||
|
||||
self.len -= 1;
|
||||
result
|
||||
// Box gets implicitly freed here, knows there is no T.
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn front(&self) -> Option<&T> {
|
||||
unsafe {
|
||||
self.front.map(|node| &(*node.as_ptr()).elem)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn front_mut(&mut self) -> Option<&mut T> {
|
||||
unsafe {
|
||||
self.front.map(|node| &mut (*node.as_ptr()).elem)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn back(&self) -> Option<&T> {
|
||||
unsafe {
|
||||
self.back.map(|node| &(*node.as_ptr()).elem)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn back_mut(&mut self) -> Option<&mut T> {
|
||||
unsafe {
|
||||
self.back.map(|node| &mut (*node.as_ptr()).elem)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len == 0
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
// Oh look it's drop again
|
||||
while let Some(_) = self.pop_front() { }
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> Iter<T> {
|
||||
Iter {
|
||||
front: self.front,
|
||||
back: self.back,
|
||||
len: self.len,
|
||||
_boo: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> IterMut<T> {
|
||||
IterMut {
|
||||
front: self.front,
|
||||
back: self.back,
|
||||
len: self.len,
|
||||
_boo: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_iter(self) -> IntoIter<T> {
|
||||
IntoIter {
|
||||
list: self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for LinkedList<T> {
|
||||
fn drop(&mut self) {
|
||||
// Pop until we have to stop
|
||||
while let Some(_) = self.pop_front() { }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for LinkedList<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for LinkedList<T> {
|
||||
fn clone(&self) -> Self {
|
||||
let mut new_list = Self::new();
|
||||
for item in self {
|
||||
new_list.push_back(item.clone());
|
||||
}
|
||||
new_list
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Extend<T> for LinkedList<T> {
|
||||
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
|
||||
for item in iter {
|
||||
self.push_back(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromIterator<T> for LinkedList<T> {
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
let mut list = Self::new();
|
||||
list.extend(iter);
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for LinkedList<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_list().entries(self).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq> PartialEq for LinkedList<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.len() == other.len() && self.iter().eq(other)
|
||||
}
|
||||
|
||||
fn ne(&self, other: &Self) -> bool {
|
||||
self.len() != other.len() || self.iter().ne(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq> Eq for LinkedList<T> { }
|
||||
|
||||
impl<T: PartialOrd> PartialOrd for LinkedList<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.iter().partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ord> Ord for LinkedList<T> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.iter().cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash> Hash for LinkedList<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.len().hash(state);
|
||||
for item in self {
|
||||
item.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoIterator for &'a LinkedList<T> {
|
||||
type IntoIter = Iter<'a, T>;
|
||||
type Item = &'a T;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for Iter<'a, T> {
|
||||
type Item = &'a T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// While self.front == self.back is a tempting condition to check here,
|
||||
// it won't do the right for yielding the last element! That sort of
|
||||
// thing only works for arrays because of "one-past-the-end" pointers.
|
||||
if self.len > 0 {
|
||||
// We could unwrap front, but this is safer and easier
|
||||
self.front.map(|node| unsafe {
|
||||
self.len -= 1;
|
||||
self.front = (*node.as_ptr()).back;
|
||||
&(*node.as_ptr()).elem
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.len, Some(self.len))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DoubleEndedIterator for Iter<'a, T> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
if self.len > 0 {
|
||||
self.back.map(|node| unsafe {
|
||||
self.len -= 1;
|
||||
self.back = (*node.as_ptr()).front;
|
||||
&(*node.as_ptr()).elem
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> ExactSizeIterator for Iter<'a, T> {
|
||||
fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoIterator for &'a mut LinkedList<T> {
|
||||
type IntoIter = IterMut<'a, T>;
|
||||
type Item = &'a mut T;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for IterMut<'a, T> {
|
||||
type Item = &'a mut T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// While self.front == self.back is a tempting condition to check here,
|
||||
// it won't do the right for yielding the last element! That sort of
|
||||
// thing only works for arrays because of "one-past-the-end" pointers.
|
||||
if self.len > 0 {
|
||||
// We could unwrap front, but this is safer and easier
|
||||
self.front.map(|node| unsafe {
|
||||
self.len -= 1;
|
||||
self.front = (*node.as_ptr()).back;
|
||||
&mut (*node.as_ptr()).elem
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.len, Some(self.len))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DoubleEndedIterator for IterMut<'a, T> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
if self.len > 0 {
|
||||
self.back.map(|node| unsafe {
|
||||
self.len -= 1;
|
||||
self.back = (*node.as_ptr()).front;
|
||||
&mut (*node.as_ptr()).elem
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> ExactSizeIterator for IterMut<'a, T> {
|
||||
fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for LinkedList<T> {
|
||||
type IntoIter = IntoIter<T>;
|
||||
type Item = T;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Iterator for IntoIter<T> {
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.list.pop_front()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.list.len, Some(self.list.len))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DoubleEndedIterator for IntoIter<T> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.list.pop_back()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ExactSizeIterator for IntoIter<T> {
|
||||
fn len(&self) -> usize {
|
||||
self.list.len
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::LinkedList;
|
||||
|
||||
#[test]
|
||||
fn test_basic_front() {
|
||||
let mut list = LinkedList::new();
|
||||
|
||||
// Try to break an empty list
|
||||
assert_eq!(list.len(), 0);
|
||||
assert_eq!(list.pop_front(), None);
|
||||
assert_eq!(list.len(), 0);
|
||||
|
||||
// Try to break a one item list
|
||||
list.push_front(10);
|
||||
assert_eq!(list.len(), 1);
|
||||
assert_eq!(list.pop_front(), Some(10));
|
||||
assert_eq!(list.len(), 0);
|
||||
assert_eq!(list.pop_front(), None);
|
||||
assert_eq!(list.len(), 0);
|
||||
|
||||
// Mess around
|
||||
list.push_front(10);
|
||||
assert_eq!(list.len(), 1);
|
||||
list.push_front(20);
|
||||
assert_eq!(list.len(), 2);
|
||||
list.push_front(30);
|
||||
assert_eq!(list.len(), 3);
|
||||
assert_eq!(list.pop_front(), Some(30));
|
||||
assert_eq!(list.len(), 2);
|
||||
list.push_front(40);
|
||||
assert_eq!(list.len(), 3);
|
||||
assert_eq!(list.pop_front(), Some(40));
|
||||
assert_eq!(list.len(), 2);
|
||||
assert_eq!(list.pop_front(), Some(20));
|
||||
assert_eq!(list.len(), 1);
|
||||
assert_eq!(list.pop_front(), Some(10));
|
||||
assert_eq!(list.len(), 0);
|
||||
assert_eq!(list.pop_front(), None);
|
||||
assert_eq!(list.len(), 0);
|
||||
assert_eq!(list.pop_front(), None);
|
||||
assert_eq!(list.len(), 0);
|
||||
}
|
||||
}
|
||||
```
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,328 @@
|
||||
# Testing
|
||||
|
||||
好吧,我推迟了一段时间测试,因为我们都知道,我们现在是 Rust 的主人,不会再犯错了!另外,这是对一个旧 crate 的重写,所以我已经有了所有的测试。你已经看过很多测试了。它们就在这里:
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::LinkedList;
|
||||
|
||||
fn generate_test() -> LinkedList<i32> {
|
||||
list_from(&[0, 1, 2, 3, 4, 5, 6])
|
||||
}
|
||||
|
||||
fn list_from<T: Clone>(v: &[T]) -> LinkedList<T> {
|
||||
v.iter().map(|x| (*x).clone()).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_front() {
|
||||
let mut list = LinkedList::new();
|
||||
|
||||
// Try to break an empty list
|
||||
assert_eq!(list.len(), 0);
|
||||
assert_eq!(list.pop_front(), None);
|
||||
assert_eq!(list.len(), 0);
|
||||
|
||||
// Try to break a one item list
|
||||
list.push_front(10);
|
||||
assert_eq!(list.len(), 1);
|
||||
assert_eq!(list.pop_front(), Some(10));
|
||||
assert_eq!(list.len(), 0);
|
||||
assert_eq!(list.pop_front(), None);
|
||||
assert_eq!(list.len(), 0);
|
||||
|
||||
// Mess around
|
||||
list.push_front(10);
|
||||
assert_eq!(list.len(), 1);
|
||||
list.push_front(20);
|
||||
assert_eq!(list.len(), 2);
|
||||
list.push_front(30);
|
||||
assert_eq!(list.len(), 3);
|
||||
assert_eq!(list.pop_front(), Some(30));
|
||||
assert_eq!(list.len(), 2);
|
||||
list.push_front(40);
|
||||
assert_eq!(list.len(), 3);
|
||||
assert_eq!(list.pop_front(), Some(40));
|
||||
assert_eq!(list.len(), 2);
|
||||
assert_eq!(list.pop_front(), Some(20));
|
||||
assert_eq!(list.len(), 1);
|
||||
assert_eq!(list.pop_front(), Some(10));
|
||||
assert_eq!(list.len(), 0);
|
||||
assert_eq!(list.pop_front(), None);
|
||||
assert_eq!(list.len(), 0);
|
||||
assert_eq!(list.pop_front(), None);
|
||||
assert_eq!(list.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic() {
|
||||
let mut m = LinkedList::new();
|
||||
assert_eq!(m.pop_front(), None);
|
||||
assert_eq!(m.pop_back(), None);
|
||||
assert_eq!(m.pop_front(), None);
|
||||
m.push_front(1);
|
||||
assert_eq!(m.pop_front(), Some(1));
|
||||
m.push_back(2);
|
||||
m.push_back(3);
|
||||
assert_eq!(m.len(), 2);
|
||||
assert_eq!(m.pop_front(), Some(2));
|
||||
assert_eq!(m.pop_front(), Some(3));
|
||||
assert_eq!(m.len(), 0);
|
||||
assert_eq!(m.pop_front(), None);
|
||||
m.push_back(1);
|
||||
m.push_back(3);
|
||||
m.push_back(5);
|
||||
m.push_back(7);
|
||||
assert_eq!(m.pop_front(), Some(1));
|
||||
|
||||
let mut n = LinkedList::new();
|
||||
n.push_front(2);
|
||||
n.push_front(3);
|
||||
{
|
||||
assert_eq!(n.front().unwrap(), &3);
|
||||
let x = n.front_mut().unwrap();
|
||||
assert_eq!(*x, 3);
|
||||
*x = 0;
|
||||
}
|
||||
{
|
||||
assert_eq!(n.back().unwrap(), &2);
|
||||
let y = n.back_mut().unwrap();
|
||||
assert_eq!(*y, 2);
|
||||
*y = 1;
|
||||
}
|
||||
assert_eq!(n.pop_front(), Some(0));
|
||||
assert_eq!(n.pop_front(), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterator() {
|
||||
let m = generate_test();
|
||||
for (i, elt) in m.iter().enumerate() {
|
||||
assert_eq!(i as i32, *elt);
|
||||
}
|
||||
let mut n = LinkedList::new();
|
||||
assert_eq!(n.iter().next(), None);
|
||||
n.push_front(4);
|
||||
let mut it = n.iter();
|
||||
assert_eq!(it.size_hint(), (1, Some(1)));
|
||||
assert_eq!(it.next().unwrap(), &4);
|
||||
assert_eq!(it.size_hint(), (0, Some(0)));
|
||||
assert_eq!(it.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterator_double_end() {
|
||||
let mut n = LinkedList::new();
|
||||
assert_eq!(n.iter().next(), None);
|
||||
n.push_front(4);
|
||||
n.push_front(5);
|
||||
n.push_front(6);
|
||||
let mut it = n.iter();
|
||||
assert_eq!(it.size_hint(), (3, Some(3)));
|
||||
assert_eq!(it.next().unwrap(), &6);
|
||||
assert_eq!(it.size_hint(), (2, Some(2)));
|
||||
assert_eq!(it.next_back().unwrap(), &4);
|
||||
assert_eq!(it.size_hint(), (1, Some(1)));
|
||||
assert_eq!(it.next_back().unwrap(), &5);
|
||||
assert_eq!(it.next_back(), None);
|
||||
assert_eq!(it.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rev_iter() {
|
||||
let m = generate_test();
|
||||
for (i, elt) in m.iter().rev().enumerate() {
|
||||
assert_eq!(6 - i as i32, *elt);
|
||||
}
|
||||
let mut n = LinkedList::new();
|
||||
assert_eq!(n.iter().rev().next(), None);
|
||||
n.push_front(4);
|
||||
let mut it = n.iter().rev();
|
||||
assert_eq!(it.size_hint(), (1, Some(1)));
|
||||
assert_eq!(it.next().unwrap(), &4);
|
||||
assert_eq!(it.size_hint(), (0, Some(0)));
|
||||
assert_eq!(it.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mut_iter() {
|
||||
let mut m = generate_test();
|
||||
let mut len = m.len();
|
||||
for (i, elt) in m.iter_mut().enumerate() {
|
||||
assert_eq!(i as i32, *elt);
|
||||
len -= 1;
|
||||
}
|
||||
assert_eq!(len, 0);
|
||||
let mut n = LinkedList::new();
|
||||
assert!(n.iter_mut().next().is_none());
|
||||
n.push_front(4);
|
||||
n.push_back(5);
|
||||
let mut it = n.iter_mut();
|
||||
assert_eq!(it.size_hint(), (2, Some(2)));
|
||||
assert!(it.next().is_some());
|
||||
assert!(it.next().is_some());
|
||||
assert_eq!(it.size_hint(), (0, Some(0)));
|
||||
assert!(it.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iterator_mut_double_end() {
|
||||
let mut n = LinkedList::new();
|
||||
assert!(n.iter_mut().next_back().is_none());
|
||||
n.push_front(4);
|
||||
n.push_front(5);
|
||||
n.push_front(6);
|
||||
let mut it = n.iter_mut();
|
||||
assert_eq!(it.size_hint(), (3, Some(3)));
|
||||
assert_eq!(*it.next().unwrap(), 6);
|
||||
assert_eq!(it.size_hint(), (2, Some(2)));
|
||||
assert_eq!(*it.next_back().unwrap(), 4);
|
||||
assert_eq!(it.size_hint(), (1, Some(1)));
|
||||
assert_eq!(*it.next_back().unwrap(), 5);
|
||||
assert!(it.next_back().is_none());
|
||||
assert!(it.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq() {
|
||||
let mut n: LinkedList<u8> = list_from(&[]);
|
||||
let mut m = list_from(&[]);
|
||||
assert!(n == m);
|
||||
n.push_front(1);
|
||||
assert!(n != m);
|
||||
m.push_back(1);
|
||||
assert!(n == m);
|
||||
|
||||
let n = list_from(&[2, 3, 4]);
|
||||
let m = list_from(&[1, 2, 3]);
|
||||
assert!(n != m);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ord() {
|
||||
let n = list_from(&[]);
|
||||
let m = list_from(&[1, 2, 3]);
|
||||
assert!(n < m);
|
||||
assert!(m > n);
|
||||
assert!(n <= n);
|
||||
assert!(n >= n);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ord_nan() {
|
||||
let nan = 0.0f64 / 0.0;
|
||||
let n = list_from(&[nan]);
|
||||
let m = list_from(&[nan]);
|
||||
assert!(!(n < m));
|
||||
assert!(!(n > m));
|
||||
assert!(!(n <= m));
|
||||
assert!(!(n >= m));
|
||||
|
||||
let n = list_from(&[nan]);
|
||||
let one = list_from(&[1.0f64]);
|
||||
assert!(!(n < one));
|
||||
assert!(!(n > one));
|
||||
assert!(!(n <= one));
|
||||
assert!(!(n >= one));
|
||||
|
||||
let u = list_from(&[1.0f64, 2.0, nan]);
|
||||
let v = list_from(&[1.0f64, 2.0, 3.0]);
|
||||
assert!(!(u < v));
|
||||
assert!(!(u > v));
|
||||
assert!(!(u <= v));
|
||||
assert!(!(u >= v));
|
||||
|
||||
let s = list_from(&[1.0f64, 2.0, 4.0, 2.0]);
|
||||
let t = list_from(&[1.0f64, 2.0, 3.0, 2.0]);
|
||||
assert!(!(s < t));
|
||||
assert!(s > one);
|
||||
assert!(!(s <= one));
|
||||
assert!(s >= one);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let list: LinkedList<i32> = (0..10).collect();
|
||||
assert_eq!(format!("{:?}", list), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
|
||||
|
||||
let list: LinkedList<&str> = vec!["just", "one", "test", "more"]
|
||||
.iter().copied()
|
||||
.collect();
|
||||
assert_eq!(format!("{:?}", list), r#"["just", "one", "test", "more"]"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hashmap() {
|
||||
// Check that HashMap works with this as a key
|
||||
|
||||
let list1: LinkedList<i32> = (0..10).collect();
|
||||
let list2: LinkedList<i32> = (1..11).collect();
|
||||
let mut map = std::collections::HashMap::new();
|
||||
|
||||
assert_eq!(map.insert(list1.clone(), "list1"), None);
|
||||
assert_eq!(map.insert(list2.clone(), "list2"), None);
|
||||
|
||||
assert_eq!(map.len(), 2);
|
||||
|
||||
assert_eq!(map.get(&list1), Some(&"list1"));
|
||||
assert_eq!(map.get(&list2), Some(&"list2"));
|
||||
|
||||
assert_eq!(map.remove(&list1), Some("list1"));
|
||||
assert_eq!(map.remove(&list2), Some("list2"));
|
||||
|
||||
assert!(map.is_empty());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
现在是关键时刻:
|
||||
|
||||
```text
|
||||
cargo test
|
||||
Finished test [unoptimized + debuginfo] target(s) in 0.00s
|
||||
Running unittests src\lib.rs
|
||||
|
||||
running 12 tests
|
||||
test test::test_basic ... ok
|
||||
test test::test_basic_front ... ok
|
||||
test test::test_eq ... ok
|
||||
test test::test_iterator ... ok
|
||||
test test::test_iterator_mut_double_end ... ok
|
||||
test test::test_ord_nan ... ok
|
||||
test test::test_iterator_double_end ... ok
|
||||
test test::test_mut_iter ... ok
|
||||
test test::test_rev_iter ... ok
|
||||
test test::test_hashmap ... ok
|
||||
test test::test_ord ... ok
|
||||
test test::test_debug ... ok
|
||||
|
||||
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||
$env:MIRIFLAGS="-Zmiri-tag-raw-pointers"
|
||||
cargo miri test
|
||||
Compiling linked-list v0.0.3
|
||||
Finished test [unoptimized + debuginfo] target(s) in 0.35s
|
||||
Running unittests src\lib.rs
|
||||
|
||||
running 12 tests
|
||||
test test::test_basic ... ok
|
||||
test test::test_basic_front ... ok
|
||||
test test::test_debug ... ok
|
||||
test test::test_eq ... ok
|
||||
test test::test_hashmap ... ok
|
||||
test test::test_iterator ... ok
|
||||
test test::test_iterator_double_end ... ok
|
||||
test test::test_iterator_mut_double_end ... ok
|
||||
test test::test_mut_iter ... ok
|
||||
test test::test_ord ... ok
|
||||
test test::test_ord_nan ... ok
|
||||
test test::test_rev_iter ... ok
|
||||
|
||||
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
😭
|
||||
|
||||
我们做到了,我们真的没有搞砸。这不是小把戏!我们所有的练习和训练终于值得了!我们终于写出了好代码
|
||||
|
||||
现在,我们可以回到 "有趣的事情 "上来了!
|
Loading…
Reference in new issue