use std::{mem, ptr};
pub struct DList<T> {
dummy: DNode<T>,
}
struct DNode<T> {
data: T,
next: *mut DNode<T>,
prev: *mut DNode<T>,
}
impl<T> DList<T> {
pub fn new() -> DList<T> {
unsafe {
let mut dummy = DNode {
data: mem::zeroed(),
next: ptr::null_mut(),
prev: ptr::null_mut(),
};
dummy.prev = &mut dummy;
dummy.next = &mut dummy;
DList { dummy }
}
}
pub fn push_front(&mut self, data: T) {
let mut new_node = DNode {
data,
next: ptr::null_mut(),
prev: ptr::null_mut(),
};
let old_front = self.dummy.next;
new_node.prev = &mut self.dummy;
self.dummy.next = &mut new_node;
new_node.next = old_front;
unsafe {
(*old_front).prev = &mut new_node;
}
}
pub fn pop_front(&mut self) -> T {
let front = self.dummy.next;
let new_front = unsafe { (*front).next };
self.dummy.next = new_front;
unsafe {
(*new_front).prev = &mut self.dummy;
front.read().data
}
}
}
I tried to implement a circular doubly linked list with unsafe rust. I design my data structure with pointers and decided to have a dummy node for convenience.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_push_front_pop_front() {
let mut dlist = DList::new();
dlist.push_front(1);
dlist.push_front(2);
dlist.push_front(3);
assert_eq!(dlist.pop_front(), 3);
assert_eq!(dlist.pop_front(), 2);
assert_eq!(dlist.pop_front(), 1);
}
}
But when testing it, the doubly linked list does not pop the elements in the correct way. It always pops the last element added to it, which in this case is 3. I expected it to pop 3, 2 ,1, but it keeps popping 3.
I wonder if I'm using unsafe rust in a wrong way.
data: mem::zeroed(). You can usezeroedforMaybeUninit<T>but not forT. It's not the problem in your specific test, though, since 0 is valid fori32.