I am trying to learn rust and would appreciate help in how to write the following snippet in 100% safe rust. This is a is doubly linked list for some specific use case and therefore it does not have all the methods one would typically expect.
#![feature(box_into_raw_non_null)]
use core::ptr::NonNull;
use core::marker::PhantomData;
use std::{ iter::Iterator, boxed::Box};
use std::mem;
extern crate core;
struct Dll<T> {
head: Option<NonNull<Node<T>>>,
tail: Option<NonNull<Node<T>>>,
len: usize,
marker: PhantomData<Box<Node<T>>>
}
struct Node<T> {
next: Option<NonNull<Node<T>>>,
prev: Option<NonNull<Node<T>>>,
data: T
}
impl<T> Node<T> {
pub fn new(data: T) -> Self {
Node {
next: None,
prev: None,
data
}
}
}
impl<T: Default> Dll<T> {
pub fn new() -> Self {
Dll {
head: None,
tail: None,
len: 0,
marker: PhantomData,
}
}
/// Always adds the element to the end of the list.
pub fn append(&mut self, data: T) -> NonNull<Node<T>> {
let mut node = Node::new(data);
node.prev = self.tail;
node.next = None;
let boxed_node = Box::new(node);
let non_null_boxed_node = Box::into_raw_non_null(boxed_node);
let opt_node = Some(non_null_boxed_node);
match self.tail {
Some(mut tail) => {
unsafe {
tail.as_mut().next = opt_node;
}
}
None => self.head = opt_node,
}
self.tail = opt_node;
self.len += 1;
non_null_boxed_node
}
fn unlink_node(&mut self, mut in_node: NonNull<Node<T>>) -> T {
let node;
unsafe {
node = in_node.as_mut();
}
match node.prev {
Some(mut prev) => unsafe {
prev.as_mut().next = node.next.clone()
},
None => self.head = node.next.clone()
}
match node.next {
Some(mut next) => unsafe {
next.as_mut().prev = node.prev.clone()
},
None => self.tail = node.prev.clone()
}
self.len -= 1;
mem::replace(&mut node.data, T::default())
}
/// This works in two folds:
/// - remove the element from its old position, this might require to adjust
/// its previous and next to point to each other with some changes to tail
/// and head if required.
/// - then move it to the end of the list.
pub fn move_to_end(&mut self, node: NonNull<Node<T>>) ->
NonNull<Node<T>> {
let data = self.unlink_node(node);
self.append(data)
}
pub fn iter(&self) -> Iter<'_, T> {
Iter {
head: self.head,
len: self.len,
marker: PhantomData,
}
}
}
pub struct Iter<'a, T: 'a> {
head: Option<NonNull<Node<T>>>,
len: usize,
marker: PhantomData<&'a Node<T>>,
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
#[inline]
fn next(&mut self) -> Option<&'a T> {
if self.len == 0 {
None
} else {
self.head.map(|non_null_node| {
// Need an unbound lifetime to get 'a
let node: &Node<T>;
unsafe {
node = &*non_null_node.as_ptr();
}
self.len -= 1;
self.head = node.next;
&node.data
})
}
}
}
#[test]
fn test() {
let mut list = Dll::new();
let node1 = list.append(1);
let _node2 = list.append(2);
let _node3 = list.append(3);
assert_eq!(vec![&1, &2, &3], list.iter().collect::<Vec<_>>());
list.move_to_end(node1);
assert_eq!(vec![&2, &3, &1], list.iter().collect::<Vec<_>>());
}
How can I remove the unsafe parts with safe rust ?