Quick Ref: Smart Pointers ​
Updated Mar 2026When to Use Which ​
Comparison Table ​
| Pointer | Thread-safe | Shared | Mutable | Runtime cost |
|---|---|---|---|---|
Box<T> | Yes (if T: Send) | No | Yes (if mut) | Heap allocation |
Rc<T> | No | Yes | No | Reference counting |
Arc<T> | Yes | Yes | No | Atomic ref counting |
RefCell<T> | No | No | Yes (runtime check) | Borrow tracking |
Mutex<T> | Yes | Via Arc | Yes (lock) | Lock overhead |
RwLock<T> | Yes | Via Arc | Yes (write lock) | Lock overhead |
Cow<'a, T> | Depends | No | Clone on write | Possible clone |
Weak<T> | Same as parent | Yes | No | Upgrade check |
Box<T> — Heap Allocation ​
rust
// Put data on the heap
let boxed = Box::new(42);
println!("{}", *boxed); // Deref to access inner value
// Required for recursive types
enum List {
Cons(i32, Box<List>),
Nil,
}
use List::{Cons, Nil};
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
// Trait objects
let shapes: Vec<Box<dyn Draw>> = vec![
Box::new(Circle { radius: 5.0 }),
Box::new(Rectangle { width: 3.0, height: 4.0 }),
];Rc<T> — Reference Counted (Single-Thread) ​
rust
use std::rc::Rc;
let a = Rc::new(vec![1, 2, 3]);
let b = Rc::clone(&a); // Increments count (cheap!)
let c = Rc::clone(&a);
println!("Count: {}", Rc::strong_count(&a)); // 3
println!("Data: {:?}", a); // All three see same data
// b and c dropped here — count decremented
// When count reaches 0, data is freedArc<T> — Atomic Reference Counted (Thread-Safe) ​
rust
use std::sync::Arc;
use std::thread;
let data = Arc::new(vec![1, 2, 3]);
let mut handles = vec![];
for i in 0..3 {
let data = Arc::clone(&data);
handles.push(thread::spawn(move || {
println!("Thread {i}: {:?}", data);
}));
}
for h in handles {
h.join().unwrap();
}RefCell<T> — Interior Mutability ​
rust
use std::cell::RefCell;
let data = RefCell::new(vec![1, 2, 3]);
// Immutable borrow
{
let borrowed = data.borrow();
println!("{:?}", *borrowed);
}
// Mutable borrow
{
let mut borrowed = data.borrow_mut();
borrowed.push(4);
}
// PANICS at runtime if rules violated:
// let r1 = data.borrow();
// let r2 = data.borrow_mut(); // PANIC: already borrowed immutablyRc<RefCell<T>> — Shared + Mutable (Single-Thread) ​
rust
use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug)]
struct Node {
value: i32,
children: Vec<Rc<RefCell<Node>>>,
}
let leaf = Rc::new(RefCell::new(Node {
value: 3,
children: vec![],
}));
let branch = Rc::new(RefCell::new(Node {
value: 5,
children: vec![Rc::clone(&leaf)],
}));
// Mutate through shared reference
leaf.borrow_mut().value = 10;
println!("Leaf: {:?}", leaf.borrow());
println!("Branch child: {:?}", branch.borrow().children[0].borrow());Arc<Mutex<T>> — Shared + Mutable (Multi-Thread) ​
rust
use std::sync::{Arc, Mutex};
use std::thread;
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
handles.push(thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
}));
}
for h in handles { h.join().unwrap(); }
println!("Final: {}", *counter.lock().unwrap()); // 10Cow<T> — Clone on Write ​
rust
use std::borrow::Cow;
fn process(input: &str) -> Cow<str> {
if input.contains("bad") {
// Only allocates when modification needed
Cow::Owned(input.replace("bad", "good"))
} else {
// Zero-cost borrowed reference
Cow::Borrowed(input)
}
}
let clean = process("hello world"); // Borrowed, no allocation
let fixed = process("bad input"); // Owned, allocation happenedWeak<T> — Preventing Reference Cycles ​
rust
use std::cell::RefCell;
use std::rc::{Rc, Weak};
struct Node {
value: i32,
parent: RefCell<Weak<Node>>, // Weak — doesn't prevent drop
children: RefCell<Vec<Rc<Node>>>, // Strong — owns children
}
let root = Rc::new(Node {
value: 1,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});
let child = Rc::new(Node {
value: 2,
parent: RefCell::new(Rc::downgrade(&root)), // Weak reference to parent
children: RefCell::new(vec![]),
});
root.children.borrow_mut().push(Rc::clone(&child));
// Access weak reference
if let Some(parent) = child.parent.borrow().upgrade() {
println!("Parent value: {}", parent.value);
}Deref Coercion Chain ​
rust
fn greet(name: &str) {
println!("Hello, {name}!");
}
let boxed: Box<String> = Box::new(String::from("World"));
greet(&boxed); // Box<String> -> String -> str -> &str (automatic!)