Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 236 additions & 1 deletion 1_concepts/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,238 @@
use crate::list::CustomLinkedList;
use std::collections::LinkedList;

fn main() {
println!("Implement me!");
let mut playlist_custom: CustomLinkedList<String> = CustomLinkedList::new();
let mut playlist_std: LinkedList<String> = 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<T: Clone> {
inner: Arc<Mutex<InnerLinkedList<T>>>,
}
impl<T: Clone> CustomLinkedList<T> {
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<T> {
self.inner.lock().unwrap().iter()
}
}

#[derive(Debug)]
struct InnerLinkedList<T> {
head: Option<Arc<Mutex<Link<T>>>>,
tail: Option<Arc<Mutex<Link<T>>>>,
}
impl<T: Clone> InnerLinkedList<T> {
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<T> {
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<T> {
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<T> {
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<T> Iterator for Iter<T> {
type Item = T;

fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}

pub struct Iter<T> {
inner: std::vec::IntoIter<T>,
}

#[derive(Debug)]
struct Link<T> {
prev: Option<Weak<Mutex<Link<T>>>>,
next: Option<Arc<Mutex<Link<T>>>>,
data: T,
}
impl<T> Link<T> {
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]);
}
}
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down