rustrefrefcell

questions related to rust RefCell<T> and Ref


I'm grappling with why the code below fails to compile. I'm trying to create a doubly linked list with two nodes. Then access the parent node's value from child node pointer.

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

struct Node {
    val : i32,
    parent : RefCell<Option<Weak<Node>>>,
    child : RefCell<Option<Rc<Node>>>,
}

fn main() {
    let child_node = Node {
        val : 1,
        parent : RefCell::new(None),
        child : RefCell::new(None),
    };
    let child_node_ptr = Rc::new(child_node);
    
    let parent_node = Node {
        val : 2,
        parent : RefCell::new(None),
        child : RefCell::new(Some(Rc::clone(&child_node_ptr))),
    };
    let parent_node_ptr = Rc::new(parent_node);
    
    *child_node_ptr.parent.borrow_mut() = Some(Rc::downgrade(&parent_node_ptr));
    
    // this line fails to compile
    match *child_node_ptr.parent.borrow() {
        None => {},
        Some(parent_weak_ptr) => {
            match parent_weak_ptr.upgrade() {
                None => {},
                Some(parent_rc_ptr) => {
                    println!("parent node value is {}", parent_rc_ptr.val);
                },
            };
        },
    };
}

The commented like filed to compile, the error message is:

error[E0507]: cannot move out of dereference of `Ref<'_, Option<std::rc::Weak<Node>>>`
  --> src/main.rs:30:11
   |
30 |     match *child_node_ptr.parent.borrow() {
   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
31 |         None => {},
32 |         Some(parent_weak_ptr) => {
   |              ---------------
   |              |
   |              data moved here
   |              move occurs because `parent_weak_ptr` has type `std::rc::Weak<Node>`, which does not implement the `Copy` trait

My first questions is why this is the case? The way I'm looking at it, the type deduction is like follows:

child_node_ptr : Rc<Node>
child_node_ptr.parent : RefCell<Option<Weak<Node>>>
child_node_ptr.parent.borrow() : Ref<'_, T>  where T is Option<Weak<Node>>

Now since std::cell::Ref also implemented Deref trait based on Rust doc, dereferencing should give &T,

*child_node_ptr.parent.borrow() : &Option<Weak<Node>>

But in reality, when I print out the type, it is actually

*child_node_ptr.parent.borrow() : Option<Weak<Node>>

So this seems to be contradictory to what rust doc says about its Deref implementation.

fn deref(&self) -> &T

Solution

  • I figured out the answer is that Deref coercion is fighting us instead of helping us.

    *child_node_ptr.parent.borrow()
    

    becomes

    *(child_node_ptr.parent.borrow().deref())
    

    due to Deref coercion, then it becomes

    *(&Option<Weak<Node>>)
    

    and becomes

    Option<Weak<Node>>