From 328fd69059e430d253815293e16a230a87e6e0fb Mon Sep 17 00:00:00 2001 From: sunface Date: Thu, 17 Mar 2022 15:08:30 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=E8=8A=82[deque-?= =?UTF-8?q?=E6=9C=80=E7=BB=88=E4=BB=A3=E7=A0=81]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/SUMMARY.md | 2 +- src/too-many-lists/deque/final-code.md | 247 +++++++++++++++++++++++++ 2 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 src/too-many-lists/deque/final-code.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 00182480..0979e6ad 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -212,7 +212,7 @@ - [Peek](too-many-lists/deque/peek.md) - [基本操作的对称镜像](too-many-lists/deque/symmetric.md) - [迭代器](too-many-lists/deque/iterator.md) - + - [最终代码](too-many-lists/deque/final-code.md) - [Rust 性能优化 todo](profiling/intro.md) - [深入内存 todo](profiling/memory/intro.md) diff --git a/src/too-many-lists/deque/final-code.md b/src/too-many-lists/deque/final-code.md new file mode 100644 index 00000000..3547589d --- /dev/null +++ b/src/too-many-lists/deque/final-code.md @@ -0,0 +1,247 @@ +# 最终代码 +这一章真不好写( 也很难翻译... ),最终我们实现了一个 100% 安全但是功能残缺的双向链表。 + +同时在实现中,还有大量 `Rc` 和 `RefCell` 引起的运行时检查,最终会影响链表的性能。整个双向链表实现史就是一部别名和所有权的奋斗史。 + +总之,不管爱与不爱,它就这样了,特别是如果我们不在意内部的细节暴露给外面用户时。 + +而从下一章开始,我们将实现一个真正能够全盘掌控的链表,当然...通过 unsafe 代码实现! + + +```rust + +#![allow(unused)] +fn main() { +use std::rc::Rc; +use std::cell::{Ref, RefMut, RefCell}; + +pub struct List { + head: Link, + tail: Link, +} + +type Link = Option>>>; + +struct Node { + elem: T, + next: Link, + prev: Link, +} + + +impl Node { + fn new(elem: T) -> Rc> { + Rc::new(RefCell::new(Node { + elem: elem, + prev: None, + next: None, + })) + } +} + +impl List { + pub fn new() -> Self { + List { head: None, tail: None } + } + + pub fn push_front(&mut self, elem: T) { + let new_head = Node::new(elem); + match self.head.take() { + Some(old_head) => { + old_head.borrow_mut().prev = Some(new_head.clone()); + new_head.borrow_mut().next = Some(old_head); + self.head = Some(new_head); + } + None => { + self.tail = Some(new_head.clone()); + self.head = Some(new_head); + } + } + } + + pub fn push_back(&mut self, elem: T) { + let new_tail = Node::new(elem); + match self.tail.take() { + Some(old_tail) => { + old_tail.borrow_mut().next = Some(new_tail.clone()); + new_tail.borrow_mut().prev = Some(old_tail); + self.tail = Some(new_tail); + } + None => { + self.head = Some(new_tail.clone()); + self.tail = Some(new_tail); + } + } + } + + pub fn pop_back(&mut self) -> Option { + self.tail.take().map(|old_tail| { + match old_tail.borrow_mut().prev.take() { + Some(new_tail) => { + new_tail.borrow_mut().next.take(); + self.tail = Some(new_tail); + } + None => { + self.head.take(); + } + } + Rc::try_unwrap(old_tail).ok().unwrap().into_inner().elem + }) + } + + pub fn pop_front(&mut self) -> Option { + self.head.take().map(|old_head| { + match old_head.borrow_mut().next.take() { + Some(new_head) => { + new_head.borrow_mut().prev.take(); + self.head = Some(new_head); + } + None => { + self.tail.take(); + } + } + Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem + }) + } + + pub fn peek_front(&self) -> Option> { + self.head.as_ref().map(|node| { + Ref::map(node.borrow(), |node| &node.elem) + }) + } + + pub fn peek_back(&self) -> Option> { + self.tail.as_ref().map(|node| { + Ref::map(node.borrow(), |node| &node.elem) + }) + } + + pub fn peek_back_mut(&mut self) -> Option> { + self.tail.as_ref().map(|node| { + RefMut::map(node.borrow_mut(), |node| &mut node.elem) + }) + } + + pub fn peek_front_mut(&mut self) -> Option> { + self.head.as_ref().map(|node| { + RefMut::map(node.borrow_mut(), |node| &mut node.elem) + }) + } + + pub fn into_iter(self) -> IntoIter { + IntoIter(self) + } +} + +impl Drop for List { + fn drop(&mut self) { + while self.pop_front().is_some() {} + } +} + +pub struct IntoIter(List); + +impl Iterator for IntoIter { + type Item = T; + + fn next(&mut self) -> Option { + self.0.pop_front() + } +} + +impl DoubleEndedIterator for IntoIter { + fn next_back(&mut self) -> Option { + self.0.pop_back() + } +} + +#[cfg(test)] +mod test { + use super::List; + + #[test] + fn basics() { + let mut list = List::new(); + + // Check empty list behaves right + assert_eq!(list.pop_front(), None); + + // Populate list + list.push_front(1); + list.push_front(2); + list.push_front(3); + + // Check normal removal + assert_eq!(list.pop_front(), Some(3)); + assert_eq!(list.pop_front(), Some(2)); + + // Push some more just to make sure nothing's corrupted + list.push_front(4); + list.push_front(5); + + // Check normal removal + assert_eq!(list.pop_front(), Some(5)); + assert_eq!(list.pop_front(), Some(4)); + + // Check exhaustion + assert_eq!(list.pop_front(), Some(1)); + assert_eq!(list.pop_front(), None); + + // ---- back ----- + + // Check empty list behaves right + assert_eq!(list.pop_back(), None); + + // Populate list + list.push_back(1); + list.push_back(2); + list.push_back(3); + + // Check normal removal + assert_eq!(list.pop_back(), Some(3)); + assert_eq!(list.pop_back(), Some(2)); + + // Push some more just to make sure nothing's corrupted + list.push_back(4); + list.push_back(5); + + // Check normal removal + assert_eq!(list.pop_back(), Some(5)); + assert_eq!(list.pop_back(), Some(4)); + + // Check exhaustion + assert_eq!(list.pop_back(), Some(1)); + assert_eq!(list.pop_back(), None); + } + + #[test] + fn peek() { + let mut list = List::new(); + assert!(list.peek_front().is_none()); + assert!(list.peek_back().is_none()); + assert!(list.peek_front_mut().is_none()); + assert!(list.peek_back_mut().is_none()); + + list.push_front(1); list.push_front(2); list.push_front(3); + + assert_eq!(&*list.peek_front().unwrap(), &3); + assert_eq!(&mut *list.peek_front_mut().unwrap(), &mut 3); + assert_eq!(&*list.peek_back().unwrap(), &1); + assert_eq!(&mut *list.peek_back_mut().unwrap(), &mut 1); + } + + #[test] + fn into_iter() { + let mut list = List::new(); + list.push_front(1); list.push_front(2); list.push_front(3); + + let mut iter = list.into_iter(); + assert_eq!(iter.next(), Some(3)); + assert_eq!(iter.next_back(), Some(1)); + assert_eq!(iter.next(), Some(2)); + assert_eq!(iter.next_back(), None); + assert_eq!(iter.next(), None); + } +} +} +``` \ No newline at end of file