Home
Map
Rc and RefCell ExampleUse Rc and RefCell to simplify usage of a struct throughout a program with reference-counting.
Rust
This page was last reviewed on Jun 8, 2023.
Rc, RefCell. Sometimes Rust programs are complex and use a struct instance in many places. This pattern calls for reference-counting—there are many owners of the struct.
Arc
With Rc, we can use a struct throughout a program, and with RefCell, we can specify that the struct can be mutated. We call borrow_mut() to modify the struct.
Rc example. Consider this example code—it has a Bird struct and a House struct. The Bird is stored inside the House. But the Bird will be used elsewhere in the program.
So We need a Rc around the Bird. And because the Bird may be modified, we place it in a RefCell.
Info Rc and RefCell are the single-threaded equivalents of Arc and Mutex. They have a similar purpose of enabling multiple uses of data.
use std::cell::RefCell; use std::rc::Rc; struct Bird { feathers: i32 } struct House { bird_in_house: Rc<RefCell<Bird>> } fn main() { // Put reference-counted bird in the house. // ... Use RefCell in the Rc to allow the bird to be mutated. let h = House { bird_in_house: Rc::new(RefCell::new(Bird { feathers: 10 }))}; let mut b = h.bird_in_house.borrow_mut(); b.feathers += 1; println!("FEATHERS: {}", b.feathers); }
FEATHERS: 11
RefCell example. We can use RefCell without an Rc. With RefCell, we can pass a struct as a reference, and then gain write access to a field on the struct.
Here We have a container struct. In main, we pass a Container instance as a reference to the add() function.
Then In add() we use borrow_mut on the RefCell field. We can then mutate the field (we push to the vector).
vec
Finally We can use borrow() on the RefCell to read the value. In this way, we have read and write access to a field on a struct.
use std::cell::*; struct Container { items: RefCell<Vec<i32>>, } fn add(container: &Container) { // We can borrow the items and mutate from behind a reference. let mut temp = container.items.borrow_mut(); temp.push(10); } fn main() { let container = Container { items: RefCell::new(vec![]), }; // Pass as reference. add(&container); // We can access the items with borrow. println!("{:?}", container.items.borrow()); }
[10]
RefCell performance. Is there any measurable slowdown caused by using RefCell over using a mutable reference to the containing struct? This benchmark aims to find out.
Version 1 This version of the code calls borrow_mut() on a RefCell located in a struct (contained in a vector of 1000 elements).
Version 2 Here we do the same thing, but we borrow an entire Vec as a mutable reference and no RefCells are used.
Result There is a measurable loss of performance from using borrow_mut() on RefCell fields.
use std::cell::*; use std::time::*; struct Test1 { count: RefCell<usize>, } struct Test2 { count: usize, } fn main() { let mut temp1 = vec![]; for _ in 0..1000 { temp1.push(Test1 { count: RefCell::new(0), }); } let mut temp2 = vec![]; for _ in 0..1000 { temp2.push(Test2 { count: 0 }); } if let Ok(max) = "100000000".parse::<usize>() { // Version 1: use RefCell. let t0 = Instant::now(); for i in 0..max { let mut c = temp1[i % 1000].count.borrow_mut(); *c += 1; } println!("{} ms", t0.elapsed().as_millis()); // Version 2: use mutable reference of struct. let t1 = Instant::now(); for i in 0..max { let c = &mut temp2[i % 1000].count; *c += 1; } println!("{} ms", t1.elapsed().as_millis()); } }
95 ms RefCell borrow_mut() 73 ms &mut
It is possible to place all a program's mutable data in a single struct, and avoid Rc and Arc. The struct can be passed around the program's functions.
However This means the entire program must be designed with this concept in mind.
Detail With Rc and Arc, we can use a more object-based design where functions can just act upon the structs they need to know about.
Also For performance, Rc and RefCell seem to have minimal impact. You can nest many structs inside a single Rc, reducing the cost further.
Rc and RefCell enable a struct to be used in many places freely in a program with no concerns about ownership. Programs are easier to design and maintain this way.
Dot Net Perls is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.
This page was last updated on Jun 8, 2023 (edit).
Home
Changes
© 2007-2024 Sam Allen.