Rust Rc & RefCell
Rc is a smart pointer that counts its owners, so one value can have several owners at once. RefCell lets a shared value change and checks the borrow rules while the program runs.
The pain: one tail, two lists, plain Box
With Box, the tail moves into the first list. The second list is left with nothing:
enum List {
Cons(i32, Box<List>),
Nil,
}
use List::{Cons, Nil};
let tail = Cons(5, Box::new(Cons(10, Box::new(Nil))));
let a = Cons(3, Box::new(tail));
let b = Cons(4, Box::new(tail));
// error[E0382]: use of moved value: `tail`Clone the pointer, not the data
Rc::clone only raises the count. And when an Rc goes out of scope, its Drop code lowers the count by one. It does this by itself. You never lower it by hand.
use std::rc::Rc;
let shared = Rc::new(10);
let _copy = Rc::clone(&shared);
println!("owners = {}", Rc::strong_count(&shared));Two lists, one shared tail
With Box this fails: the tail moves into the first list, and the second use is error E0382 (checked). Rc::clone lets both lists point at the same tail.
let tail = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
let a = Cons(3, Rc::clone(&tail));
let b = Cons(4, Rc::clone(&tail));
println!("owners = {}", Rc::strong_count(&tail));Change the shared value with make_mut
With several owners, make_mut first clones the value, so the other owners keep the old one — clone-on-write. With one owner, it edits in place.
let mut data = Rc::new(vec![1, 2]);
let other = Rc::clone(&data);
Rc::make_mut(&mut data).push(3);
println!("data = {:?} other = {:?}", data, other);Ask for unique access with get_mut
get_mut gives Some only when no other Rc points at the value. No Weak may point at it either. It gives None while the value is shared.
let mut only = Rc::new(5);
println!("get_mut = {:?}", Rc::get_mut(&mut only));RefCell: the rules, checked live
borrow() reads and borrow_mut() writes. Two live writers do not fail to compile — the program panics while running, with the message "already borrowed" (checked).
use std::cell::RefCell;
let cell = RefCell::new(vec![1, 2]);
cell.borrow_mut().push(3);
println!("{:?}", cell.borrow());Example
use std::rc::Rc; use std::cell::RefCell;
fn main() {
let shared = Rc::new(RefCell::new(vec![1, 2]));
let clone = Rc::clone(&shared);
clone.borrow_mut().push(3);
println!("{:?} owners={}", shared.borrow(), Rc::strong_count(&shared));
}Key points
- Cell<T> is the simpler cousin for single copied values
- Rc<T> on its own gives only shared, read-only access — that limit is exactly why the Rc<RefCell<T>> pairing exists
- Their counting is not safe across threads.
- The Concurrency section introduces their thread-safe twins, Arc and Mutex.
- Rc keeps one value alive for several owners by counting those owners.
In this example, shared is an Rc pointing to a RefCell holding a Vec of [1, 2]. Rc::clone makes a second owner, clone, for the same Vec — the list itself is not copied. Through clone, borrow_mut() takes the one allowed write borrow and pushes 3. Then shared.borrow() takes a read borrow to print the Vec: [1, 2, 3]. Both owners see the change, because there is only ever one Vec. strong_count reports 2 owners. Weak, a pointer that does not count as an owner, breaks cycles in Rust Reference Cycles.
Note
Rc and RefCell are for one thread only.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
What happens if two borrow_mut() guards from one RefCell are alive at the same time?