[chg] ez-ll: A safe linked list

This commit is contained in:
ddidderr 2024-02-14 22:21:52 +01:00
commit 382c54e5f1
Signed by: ddidderr
GPG Key ID: 3841F1C27E6F0E14
4 changed files with 203 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

7
Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "ez-ll"
version = "0.1.0"

6
Cargo.toml Normal file
View File

@ -0,0 +1,6 @@
[package]
name = "ez-ll"
version = "0.1.0"
edition = "2021"
[dependencies]

189
src/main.rs Normal file
View File

@ -0,0 +1,189 @@
use std::cell::RefCell;
use std::fmt::{Debug, Display, Error, Formatter};
use std::rc::Rc;
type LLLink<T> = Option<Rc<RefCell<LLItem<T>>>>;
#[derive(Debug)]
pub struct LLItem<T>
where
T: Debug,
{
next: LLLink<T>,
prev: LLLink<T>,
val: T,
}
#[derive(Debug)]
pub struct LL<T>
where
T: Debug,
{
first: LLLink<T>,
last: LLLink<T>,
}
impl<T> LL<T>
where
T: Debug,
{
pub fn empty() -> Self {
Self {
first: None,
last: None,
}
}
pub fn push_back(&mut self, val: T) {
// create new item
let new_item = Rc::new(RefCell::new(LLItem {
next: None,
prev: self.last.clone(),
val,
}));
// we have a last element so we update its next pointer
if let Some(last) = &self.last {
last.borrow_mut().next = Some(new_item.clone());
// we don't have a last element so the list is empty
} else {
self.first = Some(new_item.clone());
}
self.last = Some(new_item);
}
pub fn push_front(&mut self, val: T) {
// create new item
let new_item = Rc::new(RefCell::new(LLItem {
next: self.first.clone(),
prev: None,
val,
}));
// we have a first element so we update its prev pointer
if let Some(first) = &self.first {
first.borrow_mut().prev = Some(new_item.clone());
// we don't have a first element so the list is empty
} else {
self.last = Some(new_item.clone());
}
self.first = Some(new_item);
}
pub fn pop_back(&mut self) -> Option<T> {
// Take the current last node out of the list
let old_last = self.last.take()?;
// Handle the case where there is a previous node before the last node
if let Some(prev_node) = old_last.borrow().prev.clone() {
prev_node.borrow_mut().next = None;
self.last = Some(prev_node);
} else {
// This was the only node in the list
self.first = None;
}
// Extract the value from the old last node and return it
let old_value = Rc::try_unwrap(old_last).ok()?.into_inner().val;
Some(old_value)
}
pub fn pop_front(&mut self) -> Option<T> {
let old_first = self.first.take()?;
if let Some(next_node) = old_first.borrow().next.clone() {
next_node.borrow_mut().prev = None;
self.first = Some(next_node);
} else {
self.last = None;
}
let old_value = Rc::try_unwrap(old_first).ok()?.into_inner().val;
Some(old_value)
}
pub fn is_empty(&self) -> bool {
self.first.is_none()
}
pub fn len(&self) -> usize {
self.iter().count()
}
pub fn iter(&self) -> LLIter<T> {
LLIter {
current: self.first.clone(),
}
}
pub fn clear(&mut self) {
while self.pop_back().is_some() {}
}
}
impl<T> Display for LL<T>
where
T: Debug + Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
for elem in self.iter() {
f.write_str(&format!("{} ", elem.borrow().val))?;
}
Ok(())
}
}
impl<T> Drop for LL<T>
where
T: Debug,
{
fn drop(&mut self) {
self.clear();
}
}
pub struct LLIter<T>
where
T: Debug,
{
current: Option<Rc<RefCell<LLItem<T>>>>,
}
impl<T> Iterator for LLIter<T>
where
T: Debug,
{
type Item = Rc<RefCell<LLItem<T>>>;
fn next(&mut self) -> Option<Self::Item> {
self.current.clone().map(|current| {
self.current = current.borrow().next.clone();
current
})
}
}
impl<T> Iterator for LLItem<T>
where
T: Debug,
{
type Item = Rc<RefCell<LLItem<T>>>;
fn next(&mut self) -> Option<Self::Item> {
self.next.clone()
}
}
fn main() {
let mut l: LL<u64> = LL::empty();
(1..=100).for_each(|nr| match nr % 2 == 0 {
true => l.push_front(nr),
false => l.push_back(nr),
});
println!("{l}");
l.iter().for_each(|e| println!("{}", e.borrow().val));
}