Rust-Programming-Cookbook/Chapter02/mut-sharing-ownership/src/list.rs

262 wiersze
6.0 KiB
Rust

//!
//! A simple singly-linked list for the Rust-Cookbook by Packt Publishing.
//!
//! Recipes covered in this module:
//! - Documenting your code
//! - Testing your documentation
//! - Writing tests and benchmarks
//!
#![doc(html_logo_url = "https://blog.x5ff.xyz/img/main/logo.png",
test(no_crate_inject, attr(allow(unused_variables), deny(warnings))))]
use std::cell::RefCell;
use std::rc::Rc;
type Link<T> = Option<Rc<RefCell<Node<T>>>>;
#[derive(Clone)]
struct Node<T> where T: Sized + Clone {
value: T,
next: Link<T>,
}
impl<T> Node<T> where T: Sized + Clone {
fn new(value: T) -> Rc<RefCell<Node<T>>> {
Rc::new(RefCell::new(Node {
value: value,
next: None,
}))
}
}
///
/// A singly-linked list, with nodes allocated on the heap using `Rc`s and `RefCell`s. Here's an image illustrating a linked list:
///
///
/// ![](https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg)
///
/// *Found on https://en.wikipedia.org/wiki/Linked_list*
///
/// # Usage
///
/// ```ignore
/// let list = List::new_empty();
/// ```
///
#[derive(Clone)]
pub struct List<T> where T: Sized + Clone {
head: Link<T>,
tail: Link<T>,
///
/// The length of the list.
///
pub length: usize,
}
impl<T> List<T> where T: Sized + Clone {
///
/// Creates a new empty list.
///
///
/// # Example
///
/// ```
/// # use mut_sharing_ownership::list::List;
/// let list: List<i32> = List::new_empty();
/// ```
///
pub fn new_empty() -> List<T> {
List { head: None, tail: None, length: 0 }
}
///
/// Appends a node to the list at the end.
///
///
/// # Panics
///
/// This never panics (probably).
///
/// # Safety
///
/// No unsafe code was used.
///
/// # Example
///
/// ```
/// use mut_sharing_ownership::list::List;
///
/// let mut list = List::new_empty();
/// list.append(10);
/// ```
///
pub fn append(&mut self, value: T) {
let new = Node::new(value);
match self.tail.take() {
Some(old) => old.borrow_mut().next = Some(new.clone()),
None => self.head = Some(new.clone())
};
self.length += 1;
self.tail = Some(new);
}
///
/// Removes the list's head and returns the result.
///
///
/// # Panics
///
/// Whenever when a node unexpectedly is `None`
///
/// # Example
///
/// ```
/// # use mut_sharing_ownership::list::List;
///
/// let mut list = List::new_empty();
/// list.append(10);
/// assert_eq!(list.pop(), Some(10));
/// ```
///
pub fn pop(&mut self) -> Option<T> {
self.head.take().map(|head| {
if let Some(next) = head.borrow_mut().next.take() {
self.head = Some(next);
} else {
self.tail.take();
}
self.length -= 1;
Rc::try_unwrap(head)
.ok()
.expect("Something is terribly wrong")
.into_inner()
.value
})
}
///
/// Splits off and returns `n` nodes as a `List<T>`.
///
/// # Arguments
///
/// `n: usize` - The number of elements after which to split the list.
///
/// # Panics
///
/// Panics when:
/// - The list is empty
/// - `n` is larger than the length
///
/// # Example
///
/// ```
/// # use mut_sharing_ownership::list::List;
///
/// let mut list = List::new_empty();
/// list.append(12);
/// list.append(11);
/// list.append(10);
/// let mut list2 = list.split(1);
/// assert_eq!(list2.pop(), Some(12));
/// assert_eq!(list.pop(), Some(11));
/// assert_eq!(list.pop(), Some(10));
/// ```
///
pub fn split(&mut self, n: usize) -> List<T> {
// Don't do this in real life. Use Results, Options, or anything that
// doesn't just kill the program
if self.length == 0 || n >= self.length - 1 {
panic!("That's not working");
}
let mut n = n;
let mut new_list = List::new_empty();
while n > 0 {
new_list.append(self.pop().unwrap());
n -= 1;
}
new_list
}
}
impl <T>Drop for List<T> where T: Clone + Sized {
fn drop(&mut self) {
while self.length > 0 {
let n = self.pop();
drop(n);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_list_new_empty() {
let mut list: List<i32> = List::new_empty();
assert_eq!(list.length, 0);
assert_eq!(list.pop(), None);
}
#[test]
fn test_list_append() {
let mut list = List::new_empty();
list.append(1);
list.append(1);
list.append(1);
list.append(1);
list.append(1);
assert_eq!(list.length, 5);
}
#[test]
fn test_list_pop() {
let mut list = List::new_empty();
list.append(1);
list.append(1);
list.append(1);
list.append(1);
list.append(1);
assert_eq!(list.length, 5);
assert_eq!(list.pop(), Some(1));
assert_eq!(list.pop(), Some(1));
assert_eq!(list.pop(), Some(1));
assert_eq!(list.pop(), Some(1));
assert_eq!(list.pop(), Some(1));
assert_eq!(list.length, 0);
assert_eq!(list.pop(), None);
}
#[test]
fn test_list_split() {
let mut list = List::new_empty();
list.append(1);
list.append(1);
list.append(1);
list.append(1);
list.append(1);
assert_eq!(list.length, 5);
let list2 = list.split(3);
assert_eq!(list.length, 2);
assert_eq!(list2.length, 3);
}
#[test]
#[should_panic]
fn test_list_split_panics() {
let mut list: List<i32> = List::new_empty();
let _ = list.split(3);
}
}