From 0e92a87719e03ae0b83d0c3beb2475bbbaa4c8a6 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Wed, 13 Mar 2019 11:11:36 -0400 Subject: [PATCH] Add unsafe queue example --- src/fifth.rs | 217 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 218 insertions(+) create mode 100644 src/fifth.rs diff --git a/src/fifth.rs b/src/fifth.rs new file mode 100644 index 0000000..e084672 --- /dev/null +++ b/src/fifth.rs @@ -0,0 +1,217 @@ +//! # An Unsafe Queue + +use std::ptr; + +pub struct List { + head: Link, + tail: *mut Node, // DANGER DANGER +} + +type Link = Option>>; + +struct Node { + elem: T, + next: Link, +} + +pub struct IntoIter(List); + +pub struct Iter<'a, T: 'a> { + next: Option<&'a Node>, +} + +pub struct IterMut<'a, T: 'a> { + next: Option<&'a mut Node>, +} + +impl List { + pub fn new() -> Self { + List{ + head: None, + tail: ptr::null_mut(), + } + } + + pub fn push(&mut self, elem: T) { + let mut new_tail = Box::new(Node { + elem, + // When you push onto the tail, your next is always None + next: None, + }); + + let raw_tail: *mut _ = &mut *new_tail; + + // .is_null checks for null, equivalent to checking for None + if !self.tail.is_null() { + // If the old tail existed, update it to point to the new tail + unsafe { + (*self.tail).next = Some(new_tail); + } + } else { + self.head = Some(new_tail); + } + + self.tail = raw_tail; + } + + pub fn pop(&mut self) -> Option { + self.head.take().map(|head| { + let head = *head; + self.head = head.next; + + if self.head.is_none() { + self.tail = ptr::null_mut(); + } + + head.elem + }) + } + + pub fn peek(&self) -> Option<&T> { + self.head.as_ref().map(|node| { + &node.elem + }) + } + + pub fn peek_mut(&mut self) -> Option<&mut T> { + self.head.as_mut().map(|node| { + &mut node.elem + }) + } + + pub fn into_iter(self) -> IntoIter { + IntoIter(self) + } + + pub fn iter(&self) -> Iter { + Iter { next: self.head.as_ref().map(|node| &**node) } + } + + pub fn iter_mut(&mut self) -> IterMut { + IterMut { next: self.head.as_mut().map(|node| &mut **node) } + } +} + +impl Drop for List { + fn drop(&mut self) { + let mut cur_link = self.head.take(); + while let Some(mut boxed_node) = cur_link { + cur_link = boxed_node.next.take(); + } + } +} + +impl Iterator for IntoIter { + type Item = T; + fn next(&mut self) -> Option { + self.0.pop() + } +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + self.next.map(|node| { + self.next = node.next.as_ref().map(|node| &**node); + &node.elem + }) + } +} + +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + fn next(&mut self) -> Option { + self.next.take().map(|node| { + self.next = node.next.as_mut().map(|node| &mut **node); + &mut node.elem + }) + } +} + +#[cfg(test)] +mod test { + use super::List; + + #[test] + fn basics() { + let mut list = List::new(); + + // Check empty list behaves right + assert_eq!(list.pop(), None); + + // Populate list + list.push(1); + list.push(2); + list.push(3); + + // check normal removal + assert_eq!(list.pop(), Some(1)); + assert_eq!(list.pop(), Some(2)); + + // Push some more just to make sure nothing's corrupted + list.push(4); + list.push(5); + + // Check normal removal + assert_eq!(list.pop(), Some(3)); + assert_eq!(list.pop(), Some(4)); + + // Check exhaustion + assert_eq!(list.pop(), Some(5)); + assert_eq!(list.pop(), None); + + // Check the exhaustion case fixed the pointer right + list.push(6); + list.push(7); + + // Check normal removal + assert_eq!(list.pop(), Some(6)); + assert_eq!(list.pop(), Some(7)); + assert_eq!(list.pop(), None); + } + + + #[test] + fn into_iter() { + let mut list = List::new(); + list.push(1); + list.push(2); + list.push(3); + + let mut iter = list.into_iter(); + assert_eq!(iter.next(), Some(1)); + assert_eq!(iter.next(), Some(2)); + assert_eq!(iter.next(), Some(3)); + assert_eq!(iter.next(), None); + } + + #[test] + fn iter() { + let mut list = List::new(); + list.push(1); + list.push(2); + list.push(3); + + let mut iter = list.iter(); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), None); + } + + #[test] + fn iter_mut() { + let mut list = List::new(); + list.push(1); + list.push(2); + list.push(3); + + let mut iter = list.iter_mut(); + assert_eq!(iter.next(), Some(&mut 1)); + assert_eq!(iter.next(), Some(&mut 2)); + assert_eq!(iter.next(), Some(&mut 3)); + assert_eq!(iter.next(), None); + } +} diff --git a/src/lib.rs b/src/lib.rs index 3770e8a..4d2d0c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,3 +2,4 @@ pub mod first; pub mod second; pub mod third; pub mod fourth; +pub mod fifth;