Last active
August 23, 2020 10:03
-
-
Save srleyva/0c2b4ca4472a4c1b396d0f0c3b0858a7 to your computer and use it in GitHub Desktop.
Simple Linked List
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::iter::FromIterator; | |
use std::mem::replace; | |
pub enum Link<T> { | |
Empty, | |
More(Box<Node<T>>) | |
} | |
pub struct Node<T> { | |
item: T, | |
link: Link<T>, | |
} | |
pub struct SimpleLinkedList<T> { | |
// Delete this field | |
// dummy is needed to avoid unused parameter error during compilation | |
head: Link<T>, | |
count: usize | |
} | |
impl<T> SimpleLinkedList<T> { | |
pub fn new() -> Self { | |
Self { | |
head: Link::Empty, | |
count: 0, | |
} | |
} | |
pub fn len(&self) -> usize { | |
self.count | |
} | |
pub fn push(&mut self, element: T) { | |
let new_node = Box::new( | |
Node { | |
item: element, | |
link: replace(&mut self.head, Link::Empty) | |
} | |
); | |
self.head = Link::More(new_node); | |
self.count += 1; | |
} | |
pub fn pop(&mut self) -> Option<T> { | |
return match replace(&mut self.head, Link::Empty) { | |
Link::Empty => None, | |
Link::More(old_head) => { | |
self.head = old_head.link; | |
self.count -= 1; | |
Some(old_head.item) | |
} | |
} | |
} | |
pub fn peek(&self) -> Option<&T> { | |
match &self.head { | |
Link::Empty => None, | |
Link::More(head) => { | |
Some(&head.item) | |
} | |
} | |
} | |
pub fn rev(mut self) -> SimpleLinkedList<T> { | |
let mut reversed_list = Self { | |
head: Link::Empty, | |
count: 0, | |
}; | |
while let Some(node) = self.pop() { | |
reversed_list.push(node) | |
} | |
reversed_list | |
} | |
} | |
impl<T> FromIterator<T> for SimpleLinkedList<T> { | |
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self { | |
let mut list = Self { | |
head: Link::Empty, | |
count: 0, | |
}; | |
for item in iter { | |
list.push(item); | |
} | |
list | |
} | |
} | |
// In general, it would be preferable to implement IntoIterator for SimpleLinkedList<T> | |
// instead of implementing an explicit conversion to a vector. This is because, together, | |
// FromIterator and IntoIterator enable conversion between arbitrary collections. | |
// Given that implementation, converting to a vector is trivial: | |
// | |
// let vec: Vec<_> = simple_linked_list.into_iter().collect(); | |
// | |
// The reason this exercise's API includes an explicit conversion to Vec<T> instead | |
// of IntoIterator is that implementing that interface is fairly complicated, and | |
// demands more of the student than we expect at this point in the track. | |
impl<T> Into<Vec<T>> for SimpleLinkedList<T> { | |
fn into(mut self) -> Vec<T> { | |
let mut list_vec = Vec::new(); | |
while let Some(node) = self.pop() { | |
list_vec.push(node); | |
} | |
list_vec.reverse(); | |
list_vec | |
} | |
} | |
mod test { | |
use super::*; | |
#[test] | |
fn test_new_list_is_empty() { | |
let list: SimpleLinkedList<u32> = SimpleLinkedList::new(); | |
assert_eq!(list.len(), 0, "list's length must be 0"); | |
} | |
#[test] | |
fn test_push_increments_length() { | |
let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new(); | |
list.push(1); | |
assert_eq!(list.len(), 1, "list's length must be 1"); | |
list.push(2); | |
assert_eq!(list.len(), 2, "list's length must be 2"); | |
} | |
#[test] | |
fn test_pop_decrements_length() { | |
let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new(); | |
list.push(1); | |
list.push(2); | |
list.pop(); | |
assert_eq!(list.len(), 1, "list's length must be 1"); | |
list.pop(); | |
assert_eq!(list.len(), 0, "list's length must be 0"); | |
} | |
#[test] | |
fn test_pop_returns_head_element_and_removes_it() { | |
let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new(); | |
list.push(1); | |
list.push(2); | |
assert_eq!(list.pop(), Some(2), "Element must be 2"); | |
assert_eq!(list.pop(), Some(1), "Element must be 1"); | |
assert_eq!(list.pop(), None, "No element should be contained in list"); | |
} | |
#[test] | |
fn test_peek_returns_reference_to_head_element_but_does_not_remove_it() { | |
let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new(); | |
assert_eq!(list.peek(), None, "No element should be contained in list"); | |
list.push(2); | |
assert_eq!(list.peek(), Some(&2), "Element must be 2"); | |
assert_eq!(list.peek(), Some(&2), "Element must be still 2"); | |
list.push(3); | |
assert_eq!(list.peek(), Some(&3), "Head element is now 3"); | |
assert_eq!(list.pop(), Some(3), "Element must be 3"); | |
assert_eq!(list.peek(), Some(&2), "Head element is now 2"); | |
assert_eq!(list.pop(), Some(2), "Element must be 2"); | |
assert_eq!(list.peek(), None, "No element should be contained in list"); | |
} | |
#[test] | |
fn test_from_slice() { | |
let mut array = vec!["1", "2", "3", "4"]; | |
let mut list: SimpleLinkedList<_> = array.drain(..).collect(); | |
assert_eq!(list.pop(), Some("4")); | |
assert_eq!(list.pop(), Some("3")); | |
assert_eq!(list.pop(), Some("2")); | |
assert_eq!(list.pop(), Some("1")); | |
} | |
#[test] | |
fn test_reverse() { | |
let mut list: SimpleLinkedList<u32> = SimpleLinkedList::new(); | |
list.push(1); | |
list.push(2); | |
list.push(3); | |
let mut rev_list = list.rev(); | |
assert_eq!(rev_list.pop(), Some(1)); | |
assert_eq!(rev_list.pop(), Some(2)); | |
assert_eq!(rev_list.pop(), Some(3)); | |
assert_eq!(rev_list.pop(), None); | |
} | |
#[test] | |
fn test_into_vector() { | |
let mut v = Vec::new(); | |
let mut s = SimpleLinkedList::new(); | |
for i in 1..4 { | |
v.push(i); | |
s.push(i); | |
} | |
let s_as_vec: Vec<i32> = s.into(); | |
assert_eq!(v, s_as_vec); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment