Smart pointers
| Box for size | |
| Rc shared read | |
| RefCell runtime checks | |
| Rc with RefCell |
Box for size
Box places one value on the heap and owns it outright. Recursive types need it because the compiler must know every size up front. A list node holds its tail inside a box, so each node measures the same few bytes.
enum List {
Node(i32, Box<List>),
End,
}
fn main() {
let items = List::Node(1, Box::new(List::End));
println!("list built");
}
Without the box the enum would contain itself forever and sizing would never end. Dereferencing a box works like a plain reference. Reach for Box first whenever ownership stays single.
Rc shared read
Rc counts shared owners of one read only value. Cloning an Rc bumps the count instead of copying the data. The string below lives until both owners leave scope, then frees itself exactly once.
use std::rc::Rc;
fn main() {
let a = Rc::new(String::from("shared"));
let b = Rc::clone(&a);
println!("{} {}", a, b);
}
Rc never allows mutation, which is what makes sharing safe. Cloning is cheap no matter how large the data grows. Threads need the atomic sibling Arc, covered in a later lesson.
RefCell runtime checks
RefCell moves borrow checking from compile time to run time. Code borrows mutably through a shared reference, and bad timing panics instead of failing to compile. The counter below starts at five and climbs by one through a mutable borrow.
use std::cell::RefCell;
fn main() {
let cell = RefCell::new(5);
*cell.borrow_mut() += 1;
println!("{}", *cell.borrow());
}
Borrow and borrow mut hand out guards that release on drop. Holding two mutable guards at once panics, so keep each borrow short. Interior mutability unlocks patterns that static borrows reject.
Rc with RefCell
Pairing Rc with RefCell gives shared ownership plus mutation. Many owners point at one cell, and any of them may update through it. The counter below climbs through the shared handle with no owner left out.
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
let shared = Rc::new(RefCell::new(0));
*shared.borrow_mut() += 1;
println!("{}", *shared.borrow());
}
The outer Rc shares while the inner cell mutates, and each layer does one job well. Single threaded code stops here, since Rc never crosses threads. The Arc plus Mutex pair brings this pattern to threaded programs.
Next: Threads
Article author: Arthur Isaev