rusttypeslinked-listownership

How to travers a Linked List in Rust?


I started coding a Linked List in rust just for practice and to learn about ownership and borrowing but I have encountered a problem in the pop_back function when I am trying to update the current pointer. Any ideas?

I have tried reading the compiler message telling me a missmatched types error but I still do not know how to solve the Problem

#[derive(Clone)]
struct Node<T> {
    val: T,
    next: Option<Box<Node<T>>>,
}

struct LinkedList<T> {
    head: Option<Box<Node<T>>>,
    tail: Option<Box<Node<T>>>,
    len: u64,
}


impl<T: Clone> Node<T> {
    pub fn new(data: T) -> Self {
        Node {
            val: data,
            next: None,
        }
    }
}

impl<T: Clone> LinkedList<T> {
    pub fn new() -> Self {
        LinkedList {
            head: None,
            tail: None,
            len: 0,
        }
    }

    pub fn push_front(&mut self, data: T) {
        let mut new_node = Box::new(Node::new(data));

        if let Some(old_head) = self.head.take() {

            new_node.next = Some(old_head);
            self.head = Some(new_node);
        } else {
            self.head = Some(new_node.clone());
            self.tail = Some(new_node);
        }
        self.len += 1;
    }

    pub fn push_back(&mut self, data: T) {

        let new_node = Box::new(Node::new(data));

        if let Some(mut tail) = self.tail.take() {
            tail.next = Some(new_node.clone());
            self.tail = Some(new_node);
        } else {
            self.head = Some(new_node.clone());
            self.tail = Some(new_node);
        }

        self.len += 1;
    }

    pub fn pop_front(&mut self) -> Option<T> {
        self.head.take().map(|old_head| {
            if let Some(next) = old_head.next {
                self.head = Some(next);
            } else {
                self.tail.take();
            }
            self.len -= 1;
            old_head.val
        })
    }

    pub fn pop_back(&mut self) -> Option<T> {

        if self.head.is_none() {
            return None;
        }
        if self.len == 1 {
            return self.pop_front();
        }

        let mut current = self.head.as_mut();

        while let Some(node) = current {

             if node.next.as_ref().map_or(false, |next| next.next.is_none()) {
                let last_node = node.next.take().unwrap();
                self.tail = Some(node.clone());
                self.len -= 1;
                return Some(last_node.val);
             }
             current = node.next.as_mut().map(|next| &mut **next); //the problem !!!!
        }
        None 
    }
    
}

Solution

  • First, this definition:

    #[derive(Clone)]
    struct Node<T> {
        val: T,
        next: Option<Box<Node<T>>>,
    }
    
    struct LinkedList<T> {
        head: Option<Box<Node<T>>>,
        tail: Option<Box<Node<T>>>,
        len: u64,
    }
    

    Just cannot work. Box is a unique pointer, you cannot have both next of the before-last and tail of the list pointing to the same value. It seems you clone() your way out of the problem, but this will just duplicate the nodes and they won't be the same.

    If you want tail, you will need to use Rc<RefCell> or raw pointers. But first, as already mentioned in the comments, make sure to follow Learn Rust With Entirely Too Many Linked Lists, also for an explanation why this (and linked lists in general) are a bad idea.

    If we remove tail (playground), we are left with the following for pop_back(), that still has your error:

    pub fn pop_back(&mut self) -> Option<T> {
        if self.head.is_none() {
            return None;
        }
        if self.len == 1 {
            return self.pop_front();
        }
    
        let mut current = self.head.as_mut();
    
        while let Some(node) = current {
            if node.next.as_ref().map_or(false, |next| next.next.is_none()) {
                let last_node = node.next.take().unwrap();
                self.len -= 1;
                return Some(last_node.val);
            }
            current = node.next.as_mut().map(|next| &mut **next); //the problem !!!!
        }
        None
    }
    

    The reason is an inconsistency: in one place you do .map(|next| &mut **next) after as_mut(), while in another you don't. So one place has type Option<&mut Box<Node<T>>>, while the other is of type Option<&mut Node<T>>.

    The fix is simple: either make both use it or both not. By the way, as_mut().map(|next| &mut **next) has a handy shortcut: as_deref_mut().