Search code examples
rustborrow-checkerownershipinterior-mutability

How to return reference to value inside Rc<RefCell<Node>>


I have the following struct in Rust:

type NodePtr = Rc<RefCell<Node>>;

struct Node { 
    value: Option<String>,

    weight: usize,
    height: usize,

    parent: Option<Weak<RefCell<Self>>>,
    left: Option<NodePtr>,
    right: Option<NodePtr>
}

I created traits trait Access and trait TreeNode and implemented them for NodePtr.

trait Access {
    fn borrow_node(&self) -> Ref<Node>;
    fn borrow_node_mut(&self) -> RefMut<Node>;
}

trait TreeNode: Sized {
    fn is_leaf(&self) -> bool;
    fn set_left_child(&self, child: Option<Self>);
    fn set_right_child(&self, child: Option<Self>);
    fn contains_left(&self, line: usize, prev_lines: usize) -> bool;
    fn contains_right(&self, line: usize, prev_lines: usize) -> bool;
    fn find_node(&self, line: usize) -> Option<(Self, Side)>;
    fn insert_into(self, node: Self, placement: Side) -> Self;
    fn get_line(&self, line: usize) -> Option<&String>;
}
impl Access for NodePtr
impl TreeNode for NodePtr

One of the methods is the following:

fn find_node(&self, line: usize) -> Option<(Rc<RefCell<Node>>, Side)>;

This function returns the parent of the desired note, and the Side enum contains information about whether the left or right child is the one (designed this way so that nodes are easily replacable).

Now is where I ran into trouble. I can't seem to be able to return a reference to a string contained by a node, making the data inside effectively inaccessible.

Another method of NodePtr is the find function:

fn get_line(&self, line: usize) -> Option<Ref<String>> {
    if let Some((node, side)) = self.find_node(line) {
        let child = match side {
            Side::Left => Rc::clone(node.borrow_node().left.as_ref().unwrap()),
            Side::Right => Rc::clone(node.borrow_node().right.as_ref().unwrap())
        };
        return Some(Ref::map(node.borrow(), |n| n.value.as_ref().unwrap()));
    }
    None
}

and this is where I get the error:

error[E0515]: cannot return value referencing local variable 'node'
   --> src\blahblah.rs:152:20
    |
152 |             return Some(Ref::map(node.borrow(), |n| n.value.as_ref().unwrap()));
    |                    ^^^^^^^^^^^^^^----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                    |             |
    |                    |             'node' is borrowed here
    |                    returns a value referencing data owned by the current function

And I can't seem to figure out a way to get a reference to the owned data in any shape or form.

What could work, is returning an Option<Ref<Option>>, but this is ridicoulus, and I also don't know how to fit None into a reference.

Help would be much appreciated.

I tried returning an Option<Ref>, and an Option<&String> in multiple ways and failed. I cannot get past the borrow checker.


Solution

  • You cannot do that. You've stepped into one of the shortcoming of using Rc<RefCell> graph.

    The closes you can do is to return the whole node, possibly using some wrapper to only allow getting the length.

    Or you can use some library that allows you to project Rc similarly to how you project RefCell, for example mappable-rc, but this still requires you to change the return type of the method.

    A better way is to use a graph library, for example petgraph.