Skip to content

Instantly share code, notes, and snippets.

@seungha-kim
Last active April 29, 2021 00:54
Show Gist options
  • Save seungha-kim/0c40034aaea7c206267bffa657cd56d6 to your computer and use it in GitHub Desktop.
Save seungha-kim/0c40034aaea7c206267bffa657cd56d6 to your computer and use it in GitHub Desktop.
Simple doubly linked list written in unsafe Rust
use std::ptr::{null_mut};
struct Node<T> {
value: T,
prev_ptr: *mut Node<T>,
next_ptr: *mut Node<T>,
}
impl<T> Node<T> {
fn new(value: T) -> Self {
Self {
value,
prev_ptr: null_mut(),
next_ptr: null_mut(),
}
}
}
pub struct MyLinkedList<T> {
front_ptr: *mut Node<T>,
back_ptr: *mut Node<T>,
}
impl<T> MyLinkedList<T> {
pub fn new() -> Self {
Self {
front_ptr: null_mut(),
back_ptr: null_mut(),
}
}
pub fn iter(&self) -> Iter<'_, T> {
Iter {
next: unsafe {
self.front_ptr.as_ref()
}
}
}
pub fn iter_reverse(&self) -> IterReverse<'_, T> {
IterReverse {
next: unsafe {
self.back_ptr.as_ref()
}
}
}
pub fn push_front(&mut self, value: T) {
let new_node_ptr = Box::into_raw(Box::new(Node::new(value)));
if self.back_ptr.is_null() {
self.back_ptr = new_node_ptr;
}
unsafe {
(*new_node_ptr).next_ptr = self.front_ptr;
self.front_ptr.as_mut().map(|f| f.prev_ptr = new_node_ptr);
}
self.front_ptr = new_node_ptr;
}
pub fn pop_front(&mut self) -> Option<T> {
unsafe {
self.front_ptr.as_mut().map(|front_ref| {
let front_node = Box::from_raw(self.front_ptr);
// process Node
front_ref.next_ptr.as_mut().map(|n| n.prev_ptr = null_mut());
// process LinkedList
if self.front_ptr == self.back_ptr {
self.back_ptr = null_mut();
}
self.front_ptr = front_ref.next_ptr;
front_node.value
})
}
}
}
pub struct Iter<'a, T> {
next: Option<&'a Node<T>>,
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
self.next.map(|n| {
unsafe {
self.next = n.next_ptr.as_ref();
}
&n.value
})
}
}
pub struct IterReverse<'a, T> {
next: Option<&'a Node<T>>,
}
impl<'a, T> Iterator for IterReverse<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
self.next.map(|n| {
unsafe {
self.next = n.prev_ptr.as_ref();
}
&n.value
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let mut l = MyLinkedList::new();
l.push_front(1);
l.push_front(2);
l.push_front(3);
l.push_front(4);
l.push_front(5);
l.pop_front();
l.pop_front();
let mut iter = l.iter();
assert_eq!(iter.next(), Some(&3));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), None);
assert_eq!(l.iter_reverse().collect::<Vec<_>>(), vec![&1_i32, &2, &3]);
}
}
@seungha-kim
Copy link
Author

  • pointer::as_mut 로 null check를 편하게
  • Box::from_raw 로 메모리 해제를 편하게

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment