(Beginner question) Looping through LinkedList w/ Rc and RefCell

struct MyLinkedList<A> {
    elem: A,
    next: Rc<RefCell<Option<MyLinkedList<A>>>>,
}

impl <A> MyLinkedList<A> {
    fn append(&self, new_element: A) {
        // Pull things into a bunch of different variables to understand
        // compiler errors without seeing a bunch of temp value stuff
        let mut current: &MyLinkedList<A> = self;
        let mut rc_clone: Rc<RefCell<Option<MyLinkedList<A>>>>;
        let mut rc_clone_borrow: Ref<Option<MyLinkedList<A>>>;
        loop {
            rc_clone = Rc::clone(&current.next);
            rc_clone_borrow = rc_clone.borrow();
            if let Some(next_node) = rc_clone_borrow.as_ref() {
                current = next_node;
            } else {
                break;
            }
        }
        *Rc::clone(&current.next).borrow_mut() = Some(
            MyLinkedList {
                elem: new_element,
                next: Rc::new(RefCell::new(None)),
            }
        );
    }
}

(Playground)

I'm fairly new to Rust, and so am a little confused by the errors I'm getting for this snippet of code.
I get some basic things about borrowing, at least, but I'm curious if there's a way to circumvent what I'm running into.

rc_clone is borrowed by the rc_clone.borrow() line (implicitly when doing deref coercion).
Then, rc_clone_borrow gets borrowed with deref coercion on the rc_clone_borrow.as_ref() line.

I guess I'm curious, is there some way to structure this code (without altering the struct definition) to avoid running into these borrowing errors? Or is this inherent with the way that I've structured things?

The basic problem you're running into is that rc_clone is being reassigned before rc_clone_borrow, since rc_clone_borrow borrows from rc_clone. Since the borrow can be used when rc_clone_borrow is dropped, the borrow checker can't allow that.

The way I would fix it is to just not use a reference for the current variable, and instead keep track of the cloned Rc that we need to visit next.

impl<A> MyLinkedList<A> {
    fn append(&self, new_element: A) {
        // Pull things into a bunch of different variables to understand
        // compiler errors without seeing a bunch of temp value stuff
        let mut next: Rc<RefCell<Option<MyLinkedList<A>>>> = self.next.clone();

        loop {
            let next_node = {
                // Scoped in a block so borrow drops before we assign to next below.
                let borrow: Ref<Option<MyLinkedList<A>>> = next.borrow();

                if let Some(next_node) = borrow.as_ref() {
                    next_node.next.clone()
                } else {
                    break;
                }
            };

            // The borrow has ended, so we can assign safely here.
            next = next_node;
        }

        *next.borrow_mut() = Some(MyLinkedList {
            elem: new_element,
            next: Rc::new(RefCell::new(None)),
        });
    }
}
1 Like

Ah, so it was something that simple? Thank you for putting the effort into fixing that and showing me a better way to structure things. Much appreciated.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.