RefCell+Rc, Rc+RefCell, and Dereferencing

I'm trying to better understand some borrowed code by recreating a minimal version that I can follow. But I have no idea if these patterns are sane or idiomatic:

V1: (similar to what I'm using now)
Playground: Link
Reference: Design Patterns in Rust: Mediator
Reference: Wasm_Bindgen: TODO MVC (e.g. the lib and scheduler)
Has:

  • Rc<RefCell<Option<T>>> in the mediator field
  • &*self.leaf_1.borrow_mut()

V2: (modifed example from rust docs)
Playground: Link
Reference: Rust Docs: Reference Cycles Can Leak Memory
Has:

  • RefCell<Rc<Option<T>>> in the branch field
  • Weak, Downgrade/Upgrade
  • &**self.leaf_1.borrow_mut()

Both versions compile and lint OK.

What I need help with:

  1. The Rc+RefCell / RefCell+Rc thing. -- I don't know if one structure is better than the other for the fields in the Mediator / Branch, or if the ordering will cause problems down the line.
  2. &* and &** to get references to the Rc items: -- It took an embarrasing amout of time to figure out how to get these references - but the double / triple operators makes me think I'm not accessing these items properly / idiomatically / explicitly.

Are there any rules of thumb I should be following when constucting these Rc items and getting references to them?

I am pretty sure you want Rc<RefCell<..>> as the sharing implied by the Rc layer means you will usually only get a shared reference to its interior and only via the additional interior mutability of RefCell will you be able to mutably access the actual Option<T>.

Or to put it differently, RefCell<Rc<..>> means that you share references to the whole thing, so that via RefCell you get exclusive access to a reference-counted pointer. Usually, the reference counting is used for sharing ownership and RefCell is used to recover exclusive access.

V2.1
Playground: Link

I was able to swap the RefCell+Rc with Rc+RefCell on the Rust Doc version, with the side effect of only needing to use one asterisk [ &*self.leaf_1.borrow_mut() instead of &**self.leaf_1.borrow_mut() ] - so I gues that is better.

Rc<RefCell<T>> allows you to get a mutable reference to T by locking the cell. Changes to T will be visible to all clones of the Rc.

RefCell<Rc<T>> does not allow you to get such a mutable reference (unless the Rc has a ref-count of 1, which it typically doesn't). You could use the RefCell to swap out the Rc itself while only having a shared reference, but that would only affect this value, not not all the other values that shared (clones of) the original Rc.

Your posts seem to be talking about shared references &T instead of mutable references &mut T. If you don't need mutability, you can just use Rc<T> without a cell. You can still swap out the Rc itself, if you have a mutable reference to it.

you can just use Rc<T> without a cell.

OK. I tried removing all the RefCell references from Version 2. It looks like it might work but there is still one error I'm trying to fix:

V2.2 - Playground: Link

error[E0614]: type `std::rc::Weak<Branch>` cannot be dereferenced

112 |         *leaf_1_ref.branch = Rc::downgrade(&branch);
    |         ^^^^^^^^^^^^^^^^^^ can't be dereferenced

Edit: V2.3 - Playground Link
I think the Branch still needs mutable references / RefCell - to add the Leaf nodes - but it's OK that the Leafs don't.

struct Leaf1 {
    branch: RefCell<Weak<Branch>>, // RefCell for Branch
}
struct Branch {
    leaf_1: Rc<Option<Leaf1>>, // no RefCell for Leaf
    leaf_2: Rc<Option<Leaf2>>, // no RefCell for Leaf
}

Edit: V2.4 - Playground Link
Thinking about it. The Leafs (which represent modules in the app) contain fields that need to be mutated (the Vec for event listeners, the current locale, etc.). After adding a field to the Leafs in Version 2 and trying to update it, the compiler immediately tells me I need the Rc to be mutable.

error[E0596]: cannot borrow data in an `Rc` as mutable
64 |                         if let Some(self_leaf_1) = &mut *self.leaf_1 {
   |                                                    ^^^^^^^^^^^^^^^^^ cannot borrow as mutable
   = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Option<Leaf1>>`

In general, Rc<T> introduces shared ownership of the T, but removes exclusive access to T. &mut _s would be better named exclusive references, as it is undefined behavior to have two active, aliasing &mut _s -- two &mut _s you can use at the same time which point to the same memory.

Because Rc<T> removes exclusive access -- you can have multiple Rc<T>s that give access to the same T -- they cannot hand out &mut Ts.[1] If you need exclusive access -- if you need &mut T -- then Rc<T> on its own won't do the job. You need some way to reintroduce the exclusive access.

RefCell<T> allows for shared mutation of T. It allows getting a &mut T from a &RefCell<T> by using runtime checks to ensure there is never more than one &mut T to the same value.

So when you need to have shared ownership of T but also need to be able to get &mut T, you are generally going to want Rc<RefCell<T>>, or something analogous (Arc<Mutex<T>>, etc).

Here's a longer article about all of that:

The example in the book has RefCell<Vec<Rc<Node>>> inside the Node. The RefCell allows &mut _ access to the Vec<_>, even when the Node is in an Rc<_> -- as they recursively are. It does not allow &mut _ access to the value: i32, or to a Node as a whole, since it is only part of the children field.

The example could be rewritten to have a Vec<Rc<RefCell<Node>>> instead. If one were to do so, it would be possible to get &mut _ access to the value field and to the Nodes as a whole.


So, you should consider what you need &mut _ access to, and think about the "path" to those values through your data structures, keeping in mind that Rc<_> removes the exclusive access and RefCell restores it.

In your latest playground, it looks like you want to be able to modifiy both of these fields:

struct Leaf1 {
    branch: RefCell<Weak<Branch>>,
    count: u8,
}

You could add a RefCell around the u8 or use AtomicU8, and turn Leaf1's methods back into &self methods. But perhaps what this really means is that you want a RefCell around the entire Leaf1 so you can use your &mut self methods, instead of just the one field inside Leaf1:

struct Leaf1 {
    branch: Weak<Branch>,
    count: u8,
}

struct Branch {
    leaf_1: Rc<Option<RefCell<Leaf1>>>,
    leaf_2: Rc<Option<RefCell<Leaf2>>>,
}

I kept your Rc<Option<_>> structure initially. But note that this doesn't allow a &mut _ to the Option<_>. Do you ever need to change those, or not? If not, I'd suggest this instead:

struct Branch {
    leaf_1: Option<Rc<RefCell<Leaf1>>>,
    leaf_2: Option<Rc<RefCell<Leaf2>>>,
}

That way you don't have to allocate anything extra in the None case, and some things become more ergonomic in the playground.

This is another possibility:

struct Branch {
    leaf_1: Rc<RefCell<Option<Leaf1>>>,
    leaf_2: Rc<RefCell<Option<Leaf2>>>,
}

This variation does let you get a &mut _ to the Option<_>s. Whether you need that functionality or not is the sort of question Rust forces you to consider up-front, more so than other languages tend to do.


  1. Unless they confirm that no other Rc<T>s to the same value exist, and you have exclusive access to the one that does exist -- but let's ignore that niche use case. ↩︎

Thanks again for another great reply.

I'm still reading through the code and links you provided. But the following are total wins:

From:

    if let Some(leaf_1_ref) = &*leaf_1 {
        *leaf_1_ref.branch.borrow_mut() = Rc::downgrade(&branch);
    }
    if let Some(self_branch) = self.branch.borrow().upgrade() {
        self_branch.new_message(message);
    }
    if let Some(leaf_1_ref) = &*leaf_1 {
        leaf_1_ref.on_get_vec(message);
    }
    if let Some(self_leaf_1) = &**self.leaf_1.borrow_mut() {
        self_leaf_1.mix_fn(j);
    }

To:

    leaf_1.borrow_mut().branch = Rc::downgrade(&branch);

    if let Some(self_branch) = self.branch.upgrade() {
        self_branch.new_message(message);
    }

    leaf_1.borrow_mut().on_get_vec(message);

    if let Some(self_leaf_1) = &self.leaf_1 {
        self_leaf_1.borrow_mut().mix_fn(j);
    }

As a side note: the reason for the Option in the Leafs is because in Version 1, The Mediator gets constructed before the Leafs, So None is used as a placeholder. That pattern was carried over to Version 2 in case I needed to do same thing. -- But it ends up that in Version 2, the Leafs are constructed before the Branch.

struct Mediator {
    leaf_1: Rc<RefCell<Option<Leaf1>>>,
    leaf_2: Rc<RefCell<Option<Leaf2>>>,
}

Edit: I also just noticed:

<ctrl-f> * // (no results: execpt the use statements)

So all of this was accomplished without dereferencing - which is also amazing.

I kept your Rc<Option<_>> structure initially. But note that this doesn't allow a &mut _ to the Option<_>. Do you ever need to change those, or not? If not, I'd suggest this instead:

the reason for the Option in the Leafs is because in Version 1, The Mediator gets constructed before the Leafs, So None is used as a placeholder. That pattern was carried over to Version 2 in case I needed to do same thing. -- But it ends up that in Version 2, the Leafs are constructed before the Branch.

V2.5 - Playground: Link

So no, those Option wrappers are not needed. Removing them them makes the code even more ergonomic / easier to understand:

From:

// needs if-let, &*, and *
if let Some(leaf_1_ref) = &*leaf_1 {
    *leaf_1_ref.branch.borrow_mut() = Rc::downgrade(&branch);
}

// needs borrow()
if let Some(branch) = self.branch.borrow().upgrade() {
    branch.new_message(message);
}

// needs: if-let, &*
if let Some(leaf_1_ref) = &*leaf_1 {
    leaf_1_ref.on_get_vec(message);
}

// needs if-let, &**
if let Some(self_leaf_1) = &**self.leaf_1.borrow_mut() {
    self_leaf_1.mix_fn(j);
}

To:

leaf_1.borrow_mut().branch = Rc::downgrade(&branch);

if let Some(self_branch) = self.branch.upgrade() {
    self_branch.new_message(message);
}

leaf_1.borrow_mut().on_get_vec(message);

self.leaf_1.borrow_mut().mix_fn(j);