diff --git a/1_concepts/src/main.rs b/1_concepts/src/main.rs index 4722cdb..49de845 100644 --- a/1_concepts/src/main.rs +++ b/1_concepts/src/main.rs @@ -1,3 +1,238 @@ +use crate::list::CustomLinkedList; +use std::collections::LinkedList; + fn main() { - println!("Implement me!"); + let mut playlist_custom: CustomLinkedList = CustomLinkedList::new(); + let mut playlist_std: LinkedList = LinkedList::new(); + + playlist_custom.push_back("Song 1".to_owned()); + playlist_std.push_back("Song 1".to_owned()); + playlist_custom.push_back("Song 2".to_owned()); + playlist_std.push_back("Song 2".to_owned()); + playlist_custom.push_front("Intro".to_owned()); + playlist_std.push_front("Intro".to_owned()); + + for song in playlist_custom.iter() { + println!("Playing custom: {}", song); + } + for song in playlist_std.iter() { + println!("Playing std: {}", song); + } + + playlist_custom.pop_back(); + playlist_std.pop_back(); + + playlist_custom.pop_front(); + playlist_std.pop_front(); + + for song in playlist_custom.iter() { + println!("Playing custom: {}", song); + } + for song in playlist_std.iter() { + println!("Playing std: {}", song); + } +} + +pub mod list { + use std::{ + sync::{Arc, Mutex, Weak}, + }; + + #[derive(Clone)] + pub struct CustomLinkedList { + inner: Arc>>, + } + impl CustomLinkedList { + pub fn new() -> Self { + Self { + inner: Arc::new(Mutex::new(InnerLinkedList::new())), + } + } + + pub fn push_back(&mut self, data: T) { + self.inner.lock().unwrap().push_back(data); + } + + pub fn pop_back(&mut self) { + self.inner.lock().unwrap().pop_back(); + } + + pub fn pop_front(&mut self) { + self.inner.lock().unwrap().pop_front(); + } + + pub fn push_front(&mut self, data: T) { + self.inner.lock().unwrap().push_front(data); + } + + pub fn iter(&self) -> Iter { + self.inner.lock().unwrap().iter() + } + } + + #[derive(Debug)] + struct InnerLinkedList { + head: Option>>>, + tail: Option>>>, + } + impl InnerLinkedList { + fn new() -> Self { + Self { + head: None, + tail: None, + } + } + + fn push_back(&mut self, data: T) { + let link = Arc::new(Mutex::new(Link::new(data))); + + if let Some(tail) = &mut self.tail { + tail.lock().unwrap().next = Some(Arc::clone(&link)); + link.lock().unwrap().prev = Some(Arc::downgrade(&tail)); + self.tail = Some(Arc::clone(&link)); + } else { + self.head = Some(Arc::clone(&link)); + self.tail = Some(Arc::clone(&link)); + } + } + + fn push_front(&mut self, data: T) { + let link = Arc::new(Mutex::new(Link::new(data))); + + if let Some(head) = &mut self.head { + head.lock().unwrap().prev = Some(Arc::downgrade(&link)); + link.lock().unwrap().next = Some(Arc::clone(&head)); + self.head = Some(Arc::clone(&link)); + } else { + self.head = Some(Arc::clone(&link)); + self.tail = Some(Arc::clone(&link)); + } + } + + fn pop_back(&mut self) -> Option { + self.tail.take().map(|tail| { + let mut tail = tail.lock().unwrap(); + let data = tail.data.clone(); + + if let Some(prev) = tail.prev.take() { + let prev = prev.upgrade().unwrap(); + prev.lock().unwrap().next = None; + self.tail = Some(prev); + } else { + self.head = None; + self.tail = None; + } + + data + }) + } + + fn pop_front(&mut self) -> Option { + self.head.take().map(|head| { + let mut head = head.lock().unwrap(); + let data = head.data.clone(); + + if let Some(next) = head.next.take() { + next.lock().unwrap().next = None; + self.head = Some(next); + } else { + self.head = None; + self.tail = None; + } + + data + }) + } + + fn iter(&self) -> Iter { + let mut vec = Vec::new(); + let mut current = self.head.clone(); + while let Some(link) = current { + let link_guard = link.lock().unwrap(); + vec.push(link_guard.data.clone()); + current = link_guard.next.clone(); + } + Iter { + inner: vec.into_iter(), + } + } + } + impl Iterator for Iter { + type Item = T; + + fn next(&mut self) -> Option { + self.inner.next() + } + } + + pub struct Iter { + inner: std::vec::IntoIter, + } + + #[derive(Debug)] + struct Link { + prev: Option>>>, + next: Option>>>, + data: T, + } + impl Link { + fn new(data: T) -> Self { + Link { + prev: None, + data, + next: None, + } + } + } + #[cfg(test)] + mod tests { + use super::*; + use std::collections::LinkedList; + + #[test] + fn test_correctness() { + let mut custom = CustomLinkedList::new(); + let mut std = LinkedList::new(); + + custom.push_back("Song 1".to_string()); + std.push_back("Song 1".to_string()); + custom.push_back("Song 2".to_string()); + std.push_back("Song 2".to_string()); + custom.push_front("Intro".to_string()); + std.push_front("Intro".to_string()); + + let custom_vec: Vec<_> = custom.iter().collect(); + let std_vec: Vec<_> = std.iter().cloned().collect(); + assert_eq!(custom_vec, std_vec); + + custom.pop_back(); + std.pop_back(); + let custom_vec: Vec<_> = custom.iter().collect(); + let std_vec: Vec<_> = std.iter().cloned().collect(); + assert_eq!(custom_vec, std_vec); + + custom.pop_front(); + std.pop_front(); + let custom_vec: Vec<_> = custom.iter().collect(); + let std_vec: Vec<_> = std.iter().cloned().collect(); + assert_eq!(custom_vec, std_vec); + } + + #[test] + fn test_multi_threaded() { + let mut list = CustomLinkedList::new(); + list.push_back(1); + list.push_back(2); + + let handle = std::thread::spawn(move || { + list.push_back(3); + list.push_front(0); + list + }); + + let list = handle.join().unwrap(); + let vec: Vec<_> = list.iter().collect(); + assert_eq!(vec, vec![0, 1, 2, 3]); + } + } } diff --git a/README.md b/README.md index 21e941e..e795ea3 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ This project represents a hard-way step-by-step [Rust] learning course from lang Each step must be performed as a separate [PR (pull request)][PR] with an appropriate name and check-marked here in the README's schedule after completion. Each step is a [Cargo workspace member][13], so you can run/test it from the project root (i.e. `cargo run -p step_1_8`). __Consider using [rustfmt] and [Clippy] when you're writing [Rust] code.__ - [x] [0. Become familiar with Rust basics][Step 0] (3 days) -- [ ] [1. Concepts][Step 1] (2 days, after all sub-steps) +- [x] [1. Concepts][Step 1] (2 days, after all sub-steps) - [ ] [1.1. Default values, cloning and copying][Step 1.1] (1 day) - [ ] [1.2. Boxing and pinning][Step 1.2] (1 day) - [ ] [1.3. Shared ownership and interior mutability][Step 1.3] (1 day)