A good introduction of when and how to use std::rc::Rc and std::cell::RefCell


#1

So I came recently by terms of inspecting other peoples code to this two beats, but could not find a good tutorial/explanation of them. Now, from the docs I sort of understand (and please correct me if I’m wrong I need to know this) that std::rc::Rc behaves similarly (if not identical) to std::shared_ptr in C++ and Weak as the std::weak_ptr. So far so good, I think I’m following. Now as far as I understand, since RC has the method borrw_mut which gives us a mutable reference to the object. Now I don’t quite understand from the docs example - https://doc.rust-lang.org/std/rc/, why we need the RefCell and what it is achieving and what is more how it is achieving on the background? And unfrotunately I did not found a good resource on line for that.

Just to give some background on the code I was looking at I have (will simplify for the post):

struct ParseState {
    max_err_pos: usize,
    expected: ::std::collections::HashSet<&'static str>,
    context: ::std::rc::Rc<::std::cell::RefCell<ContextObjects>>,
}
struct ContextObjects {
    dummy: (),
    graph_ctx: ComputeGraph,
}

And then I have the following part of code, given that state: &mut ParseState

let _context = state.context.clone();
let mut _context = &mut *_context.borrow_mut();
let graph = &mut _context.graph_ctx;

I can’t get my head around the second line, what exactly is the second _context and what is the point of it and why if I try to return *graph if the signature is to return ComputeGraph rust gives me cannot move out of borrowed content?


#2

RefCell introduces so called “inner mutability”, that is ability to define mutability in runtime.
It’s actually RefCell provides borrow_mut() method, not Rc. Rc just provides reference counting, and a Deref implementation to transparently dereference to inner type (call it a “smart pointer”, if you like).

Your _context var is a Rc<RefCell<ContextObject>>, when you call borrow_mut() Rust can’t find the method on Rc<T>, so it transparently dereferences it to Rc's content by calling Deref::deref() impl for Rc<T>, that is provides a reference to RefCell<T>. And then it calls borrow_mut() method of RefCell<ContextObject>. Here’s desugared code:

let mut _context = &mut *_context.deref().borrow_mut();
//                                       ^-- &RefCell<ContextObject> comes from here

Something like this.

Upd: just noticed, you don’t need to make _context mut, it’s already a mutable reference.


#3

Regarding your cannot move... error, it’s because you are trying a partial move of a single field .graph_ctx from a struct you don’t own: you have only mutable reference to it, and then you try to dereference it with * operator, that is move it out of borrowed pointer.