S8.0 What This Teaches
Box<T>for single-owner heap allocation and recursive typesRc<T>for shared ownership on a single threadRefCell<T>for interior mutability with runtime borrow checkingRc<RefCell<T>>for the combination of both
S8.1 Box<T> - Heap Allocation
Box<T> stores a value on the heap and gives you a single owning
pointer to it. When the Box is dropped, the heap memory is freed:
let b = Box::new(42);
println!("{b}"); // 42 - Deref coercion lets you use it like a plain i32
println!("{}", *b); // explicit deref
Box, the
compiler cannot determine the size of a type that contains itself:
enum List {
Cons(i32, Box<List>), // Box breaks the infinite size cycle
Nil,
}
let list = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));
Box<List> has a known, fixed size (one pointer), so the compiler
accepts it.
S8.2 Rc<T> - Shared Ownership
Rc<T> (reference-counted pointer) allows multiple owners of the
same heap value on a single thread. Each Rc::clone increments a counter;
when the counter reaches zero, the value is freed:
use std::rc::Rc;
let shared = Rc::new(String::from("shared data"));
let c1 = Rc::clone(&shared); // counter becomes 2; no heap copy
let c2 = Rc::clone(&shared); // counter becomes 3
println!("count: {}", Rc::strong_count(&shared)); // 3
drop(c1);
println!("count: {}", Rc::strong_count(&shared)); // 2
Rc is read-only shared ownership - every clone points to the same
allocation and none can mutate it through Rc alone. Rc
is not safe to send across threads; use Arc<T> (atomic reference
count) when sharing across threads.
S8.3 RefCell<T> - Interior Mutability
RefCell<T>
defers those checks to runtime, allowing mutation through a shared reference:
use std::cell::RefCell;
let data = RefCell::new(vec![1, 2, 3]);
{
let mut v = data.borrow_mut(); // runtime check: panics if already borrowed
v.push(4);
} // mutable borrow released here
println!("{:?}", data.borrow()); // [1, 2, 3, 4]
borrow_mut and a
borrow active at the same time - but violations become runtime panics
instead of compile errors. Use RefCell only when the compiler's static
analysis is too conservative and you can reason that the rules are not violated at
runtime.
S8.4 Rc<RefCell<T>> - Shared Mutable State
Rc and RefCell when multiple owners need to mutate
a shared value:
use std::rc::Rc;
use std::cell::RefCell;
let counter = Rc::new(RefCell::new(0));
let c1 = Rc::clone(&counter);
let c2 = Rc::clone(&counter);
*c1.borrow_mut() += 1;
*c2.borrow_mut() += 1;
println!("{}", counter.borrow()); // 2
Arc<Mutex<T>> instead
(covered in the Threads tutorial).
S8.5 Example - All Together
// SmartPointers - demonstrates Box<T>, Rc<T>, and RefCell<T> in Rust.
use std::rc::Rc;
use std::cell::RefCell;
#[derive(Debug)]
#[allow(dead_code)]
enum List {
Cons(i32, Box<List>),
Nil,
}
fn main() {
// --- Box<T>: single-owner heap allocation ---
println!("--- Box ---");
let b = Box::new(42);
println!("boxed: {b}, deref: {}", *b);
// Box makes recursive types possible by giving the compiler a known size
let list = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));
println!("{list:?}");
// --- Rc<T>: shared ownership on a single thread ---
println!("--- Rc ---");
let shared = Rc::new(String::from("shared data"));
let c1 = Rc::clone(&shared);
let c2 = Rc::clone(&shared);
println!("value: {shared}, ref count: {}", Rc::strong_count(&shared));
drop(c1);
println!("after drop c1, count: {}", Rc::strong_count(&shared));
drop(c2);
println!("after drop c2, count: {}", Rc::strong_count(&shared));
// --- RefCell<T>: interior mutability, borrow checked at runtime ---
println!("--- RefCell ---");
let data = RefCell::new(vec![1, 2, 3]);
{
let mut v = data.borrow_mut();
v.push(4);
}
println!("{:?}", data.borrow());
// --- Rc<RefCell<T>>: shared ownership + interior mutability ---
println!("--- Rc<RefCell<T>> ---");
let counter = Rc::new(RefCell::new(0));
let c1 = Rc::clone(&counter);
let c2 = Rc::clone(&counter);
*c1.borrow_mut() += 1;
*c2.borrow_mut() += 1;
println!("counter: {}", counter.borrow());
}
--- Box ---
boxed: 42, deref: 42
Cons(1, Cons(2, Nil))
--- Rc ---
value: shared data, ref count: 3
after drop c1, count: 2
after drop c2, count: 1
--- RefCell ---
[1, 2, 3, 4]
--- Rc<RefCell<T>> ---
counter: 2
S8.6 Exercise
Exercise
- Define a recursive
Treeenum usingBox:Tree::Leaf(i32)andTree::Node(Box<Tree>, Box<Tree>). Build a small tree and write a functionsum(t: &Tree) -> i32that sums all leaf values. - Create two
Rc<String>clones of the same string. Confirm the reference count is 3 (original + 2 clones). Drop one and confirm it drops to 2. - Use
Rc<RefCell<Vec<i32>>>to share a vec between two "owners" (two separate variables). Append a value through each and print the final vec through the original.
S8.7 Common Mistakes
Using Rc across threads
use std::rc::Rc;
let r = Rc::new(42);
std::thread::spawn(move || println!("{r}")); // error: Rc cannot be sent across threads
Rc is not Send. Use Arc<T> for cross-thread shared ownership.Calling borrow_mut twice without releasing
let cell = RefCell::new(0);
let a = cell.borrow_mut();
let b = cell.borrow_mut(); // panics at runtime: already mutably borrowed
borrow_mut must be dropped before taking a second one.
Structure code so borrows are short-lived within a { } block.
Boxing unnecessarily
let x = Box::new(42_i32); // rarely useful - i32 is Copy and already small
S8.8 Key Terms
| Term | Meaning |
|---|---|
| Box<T> | Single-owner heap pointer; frees memory when dropped |
| Rc<T> | Reference-counted shared ownership; single-threaded only |
| Rc::clone | Increments the reference count; does not copy the heap data |
| Rc::strong_count | Returns the current reference count |
| RefCell<T> | Enforces borrow rules at runtime instead of compile time |
| borrow() | Returns an immutable Ref<T> guard; panics if mutably borrowed |
| borrow_mut() | Returns a mutable RefMut<T> guard; panics if any borrow is active |
| interior mutability | Mutating data through a shared reference, enforced at runtime |
| Arc<T> | Atomic reference count; like Rc but safe across threads |
| Mutex<T> | Mutual exclusion lock; pairs with Arc for shared mutable state across threads |