Box / Rc / Arc
Smart pointers in Rust wrap data with extra semantics — heap allocation (Box), shared ownership (Rc/Arc), interior mutability (RefCell/Mutex), and weak references. They let you express patterns the borrow checker alone can’t, like graphs and cycles, without unsafe code.
Box, Rc/Arc, RefCell, Mutex, weak
EXAMPLE
// 1) Box<T> — heap allocation, single owner
let b = Box::new(42);
println!("{}", *b); // 42 — dereferences automatically
// Use cases:
// • Recursive types: trees, linked lists
// • Large values you want on the heap
// • Trait objects: Box<dyn Trait>
enum List {
Cons(i32, Box<List>),
Nil,
}
use List::*;
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
// 2) Trait objects via Box
trait Shape { fn area(&self) -> f64; }
struct Circle { r: f64 }
struct Square { s: f64 }
impl Shape for Circle { fn area(&self) -> f64 { std::f64::consts::PI * self.r * self.r } }
impl Shape for Square { fn area(&self) -> f64 { self.s * self.s } }
let shapes: Vec<Box<dyn Shape>> = vec![
Box::new(Circle { r: 1.0 }),
Box::new(Square { s: 2.0 }),
];
for s in &shapes { println!("{}", s.area()); }
// 3) Rc<T> — Reference Counted, SINGLE-THREADED shared ownership
use std::rc::Rc;
let a = Rc::new(String::from("hello"));
let b = Rc::clone(&a); // increments count, NOT a deep copy
let c = Rc::clone(&a);
println!("count = {}", Rc::strong_count(&a)); // 3
// Use Rc when many owners want immutable access to the same data within ONE thread.
// 4) Arc<T> — Atomic Rc, THREAD-SAFE shared ownership
use std::sync::Arc;
use std::thread;
let data = Arc::new(vec![1, 2, 3]);
let handles: Vec<_> = (0..5).map(|i| {
let d = Arc::clone(&data);
thread::spawn(move || println!("thread {i}: {:?}", d))
}).collect();
for h in handles { h.join().unwrap(); }
// 5) RefCell<T> — INTERIOR MUTABILITY, single-threaded
use std::cell::RefCell;
let cell = RefCell::new(5);
*cell.borrow_mut() = 10; // mutable borrow at runtime
println!("{}", *cell.borrow());
// Borrow checker rules enforced at RUNTIME:
let b1 = cell.borrow();
// let b2 = cell.borrow_mut(); // PANICS — already borrowed
// Use RefCell only when:
// • Compile-time borrow checker is too strict for your pattern
// • You can prove no aliasing at runtime (e.g. tree traversal)
// 6) Rc<RefCell<T>> — shared mutable state, single-threaded
use std::rc::Rc;
use std::cell::RefCell;
let shared = Rc::new(RefCell::new(vec![1, 2, 3]));
let clone = Rc::clone(&shared);
clone.borrow_mut().push(4);
println!("{:?}", shared.borrow()); // [1, 2, 3, 4]
// 7) Arc<Mutex<T>> — shared mutable state, multi-threaded
use std::sync::{Arc, Mutex};
use std::thread;
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let c = Arc::clone(&counter);
handles.push(thread::spawn(move || {
let mut n = c.lock().unwrap();
*n += 1;
}));
}
for h in handles { h.join().unwrap(); }
println!("counter = {}", *counter.lock().unwrap()); // 10
// Other locks:
// RwLock<T> — many readers OR one writer
// AtomicI64, AtomicUsize, etc. — lock-free for primitives
// parking_lot::Mutex — faster alternative to std::sync::Mutex
// 8) Weak<T> — non-owning reference (breaks reference cycles)
use std::rc::{Rc, Weak};
use std::cell::RefCell;
struct Node {
value: i32,
parent: RefCell<Weak<Node>>,
children: RefCell<Vec<Rc<Node>>>,
}
let leaf = Rc::new(Node {
value: 3,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});
let branch = Rc::new(Node {
value: 5,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![Rc::clone(&leaf)]),
});
*leaf.parent.borrow_mut() = Rc::downgrade(&branch); // weak; doesn't bump count
// Weak::upgrade() returns Option<Rc<T>> — None if the value has been dropped.
if let Some(p) = leaf.parent.borrow().upgrade() {
println!("leaf parent value = {}", p.value);
}
// 9) Cow<T> — copy on write
use std::borrow::Cow;
fn process(input: &str) -> Cow<str> {
if input.contains(' ') {
Cow::Owned(input.replace(' ', "_")) // own + modify
} else {
Cow::Borrowed(input) // pass through
}
}
// Avoid allocations unless mutation is needed.
// 10) Pin<T> — for self-referential types and async
use std::pin::Pin;
// Used internally by Future / async fn; rarely written directly.
// Pin prevents the value from moving in memory.
// 11) Choosing the right pointer
//
// Need Single thread Multi thread
// Sole owner Box Box
// Shared, immut Rc Arc
// Shared, mut Rc<RefCell> Arc<Mutex> / Arc<RwLock>
// Many readers Rc Arc<RwLock>
// Weak link Rc + Weak Arc + Weak
// Lock-free atomic N/A AtomicXxx (numbers only)
// 12) Performance considerations
// • Box: zero overhead beyond heap alloc; deref is free
// • Rc: one heap alloc per clone of the SAME pointer is cheap; deref is free
// • Arc: atomic counter ops are slightly more expensive than Rc
// • RefCell: runtime borrow check costs ~ns per borrow
// • Mutex: blocking; on contention, schedule switch
// • RwLock: faster reads, slower writes; consider RCU patterns for read-heavy work
// 13) Common bugs
// • Refcell BorrowMutError — multiple borrows; restructure to scope each borrow
// • Cycle leaks with Rc — use Weak for parent references
// • Send + Sync issues — Rc isn't thread-safe; use Arc
// • Mutex poisoning — a thread panicked while holding the lock; recover via err.into_inner()
// • Holding a Mutex across .await — deadlocks in async code; use tokio::sync::Mutex
// • Rc + threads → compile error 'cannot send Rc<...> between threads safely'
// • Box<dyn Trait> vs &dyn Trait — Box owns + heap; &dyn borrows
// • Cloning Arc in a hot path — each clone is an atomic increment; acceptable but measure
Why it matters
Pick the smart pointer that matches your ownership model: Box for heap allocation and trait objects, Rc/Arc for shared ownership, RefCell/Mutex for interior mutability, Weak to break reference cycles. Reach for Arc<Mutex<T>> for shared mutable state across threads — and never hold a std::sync::Mutex across an .await.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
use std::rc::Rc;
let a = Rc::new(vec![1, 2, 3]);
let b = Rc::clone(&a);
println!("refs = {}", Rc::strong_count(&a));
Try it Yourself »
Discussion
Loading…