022 why do you want a non safe single linked list by learning rust through linked list

Time:2021-8-3

introduce

Video address:www.bilibili.com/video/av78062009/
Relevant source code:github.com/anonymousGiga/Rust-link…

Details

Previously, we all use safe rust programming to implement the linked list, but it is really difficult to implement it. Starting from this section, we begin to implement the linked list in a non secure programming way.

The difference between stack and queue is that stack is first in first out and queue is first in first out. Corresponding to the push and pop functions, the differences are as follows:

//Stack
input list:
[Some(ptr)] -> (A, Some(ptr)) -> (B, None)

stack push X:
[Some(ptr)] -> (X, Some(ptr)) -> (A, Some(ptr)) -> (B, None)

stack pop:
[Some(ptr)] -> (A, Some(ptr)) -> (B, None)


//Queue
input list:
[Some(ptr)] -> (A, Some(ptr)) -> (B, None)

flipped push X:
[Some(ptr)] -> (A, Some(ptr)) -> (B, Some(ptr)) -> (X, None)

flipped pop:
[Some(ptr)] -> (A, Some(ptr)) -> (B, None)

We found that when using linked lists to implement stacks and queues respectively, pop and push functions may need to be traversed. So how to improve efficiency and reduce traversal? The answer is that we record the head and tail of the linked list respectively.

Try a more perfect single linked list

In order to record the tail of the linked list, we redefine our linked list as follows:

ub struct List<'a, T> {
    head: Link<T>,
    Tail: option < & 'a mut node < T > >, // pointing to yourself, they are all variable references and are not allowed to exist
}

type Link<T> = Option<Box<Node<T>>>;

struct Node<T> {
    elem: T,
    next: Link<T>,
}

impl<'a, T> List<'a, T> {
    pub fn new() -> Self {
        List { head: None, tail: None }    
    }

    pub fn push(&'a mut self, elem: T) {
        let new_tail = Box::new(Node {
            elem: elem,
            next: None, 
        });

        let new_tail = match self.tail.take() {
            Some(old_tail) => {
                old_tail.next = Some(new_tail);
                old_tail.next.as_deref_mut()
            }
            None => {
                self.head = Some(new_tail);
                self.head.as_deref_mut()
            }
        }; 

        self.tail = new_tail;
    }

    pub fn pop(&'a mut self) -> Option<T> {
        self.head.take().map(|head| {
            let head = *head;
            self.head = head.next;

            if self.head.is_none() {
                self.tail = None;
            }

            head.elem
        })
    }
}

Existing problems

When we implement the single linked list with record tail position, we find that there is no problem in compilation. Let’s add the test code:

#[cfg(test)]
mod tests {
    use super::List;

    #[test]
    fn basics() {
        let mut list = List::new();
        assert_eq!(list.pop(), None);
        list.push(1);
        list.push(2);
        list.push(3);
        assert_eq!(list.pop(), Some(1));
        assert_eq!(list.pop(), Some(2));
        list.push(4);
        list.push(5);
        assert_eq!(list.pop(), Some(3));
        assert_eq!(list.pop(), Some(4));
        assert_eq!(list.pop(), Some(5));
        assert_eq!(list.pop(), None);
    }
}

If an error is found during compilation, what is the reason?

It turns out that in the list, the defined tail is variable, and the defined list is also variable. The tail is a part of the linked list, which is equivalent to that the variable reference points to itself. It is also variable, which is equivalent to that multiple variable references point to the same thing, so an error is reported.

According to our analysis, this problem seems to be misunderstood. How can we solve it?
thereforeLead to unsafe to solve this problem.

This work adoptsCC agreement, reprint must indicate the author and the link to this article

Linghu rushed