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


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?


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.


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.