rustborrow-checkerrefcell

Rust, how to copy the inner value out from Rc<RefCell<T>> and return it?


Brief: I'm new to Rust so I decided to practice by implementing a double linked list. For debugging purpose, I implemented the get() method, but I failed to copy the value out from a Rc<RefCell<_>>. (Sorry for asking dumb question)

Problem: I'm trying to return a Result<T, &'static str> in .get() where the T is the type of the data stored in node and &str is the error message string. The borrow checker tells me that I cannot return a reference to an in-method variable, so I tried to copy the inner value out and return it, but failed to do so.

Source code:

use std::{rc::Rc, cell::RefCell};

struct Node<T> {
    data: Option<T>,
    prev: Option<Rc<RefCell<Node<T>>>>,
    next: Option<Rc<RefCell<Node<T>>>>,
}

impl<T> Node<T> {
    /// Instantiate a new dummy node.
    /// This node is used to mark the start and end of the list.
    /// It is not counted in the size of the list.
    fn new() -> Self {
        Node {
            data: None,
            prev: None,
            next: None,
        }
    }
    /// Instantiate a new content node.
    /// This node is used to store data.
    /// It is counted in the size of the list.
    fn from(data: T) -> Self {
        Node {
            data: Some(data),
            prev: None,
            next: None,
        }
    }
}

struct List<T> {
    head: Rc<RefCell<Node<T>>>,
    tail: Rc<RefCell<Node<T>>>,
    size: usize,
}

impl<T> List<T> {
    pub fn new() -> Self {
        let head = Rc::new(RefCell::new(Node::new()));
        let tail = Rc::new(RefCell::new(Node::new()));
        head.borrow_mut().next = Some(Rc::clone(&tail));
        tail.borrow_mut().prev = Some(Rc::clone(&head));
        List { head, tail, size: 0 }
    }
    pub fn prepend(&self, data: T) {
        let node = Rc::new(RefCell::new(Node::from(data)));
        let mut head = self.head.borrow_mut();
        
        node.borrow_mut().next = Some(head.next.take().unwrap());
        node.borrow_mut().prev = Some(Rc::clone(&self.head));
        head.next = Some(Rc::clone(&node));
        if let Some(next) = node.borrow().next.as_ref() {
            next.borrow_mut().prev = Some(Rc::clone(&node));
        };
    }
    pub fn append(&self, data: T) {
        let node = Rc::new(RefCell::new(Node::from(data)));
        let mut tail = self.tail.borrow_mut();
        
        node.borrow_mut().prev = Some(Rc::clone(&tail.prev.take().unwrap()));
        node.borrow_mut().next = Some(Rc::clone(&self.tail));
        tail.prev = Some(Rc::clone(&node));
        if let Some(prev) = node.borrow().prev.as_ref() {
            prev.borrow_mut().next = Some(Rc::clone(&node));
        };
    }
    pub fn get(&self, index: isize) -> Result<T, &'static str> {
        let mut current: Rc<RefCell<Node<T>>> = Rc::clone(self.head.borrow().next.as_ref().unwrap());
        for _ in 0..index {
            let tmp = Rc::clone(current.borrow().next.as_ref().ok_or("Index out of range")?);
            current = tmp;
        }
        let result = current.borrow().data.as_ref().ok_or("Index out of range")?;  // error[E0716]
        Ok(*result)  // error[E0507]
    }
}
/*
error[E0716]: temporary value dropped while borrowed
  --> src\linked.rs:74:22
   |
74 |         let result = current.borrow().data.as_ref().ok_or("Index out of range")?;
   |                      ^^^^^^^^^^^^^^^^                                           - temporary value is freed at the end of this statement
   |                      |
   |                      creates a temporary value which is freed while still in use
75 |         Ok(*result)
   |            ------- borrow later used here
   |
help: consider using a `let` binding to create a longer lived value
   |
74 ~         let binding = current.borrow();
75 ~         let result = binding.data.as_ref().ok_or("Index out of range")?;
   |

error[E0507]: cannot move out of `*result` which is behind a shared reference
  --> src\linked.rs:75:12
   |
75 |         Ok(*result)
   |            ^^^^^^^ move occurs because `*result` has type `T`, which does not implement the `Copy` trait
*/

I've Tried:

Also: I may did these wrong, please forgive me if one of the posts solved my problem but I did not implement it in the right way, and please teach me how to do it properly. Many thanks.


Solution

  • This worked for me:

        pub fn get(&self, index: isize) -> Result<T, &'static str>
            where T: Clone
        {
            let mut current: Rc<RefCell<Node<T>>> = Rc::clone(self.head.borrow().next.as_ref().unwrap());
            for _ in 0..index {
                let tmp = Rc::clone(current.borrow().next.as_ref().ok_or("Index out of range")?);
                current = tmp;
            }
            let guard = current.borrow();
            guard.data.clone().ok_or("Index out of range")
        }
    

    (playground link)

    You need the line where T: Clone to enable the .clone() method.


    Some say it's a mistake to implement a doubly-linked list in order to learn Rust... I'm sorry to report that they are right. I did the exact same thing when I started. The other thing I tried was writing a ray-tracer; that went a lot better.

    Core data structures are implemented using raw pointers. That means writing unsafe code and putting a safe API around it: an advanced Rust skill.