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.
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).
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]
Program design notes. 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.
A summary. 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 Mar 11, 2023 (new example).