Skip to content

Quick Ref: Smart Pointers ​

Updated Mar 2026

When to Use Which ​

Comparison Table ​

PointerThread-safeSharedMutableRuntime cost
Box<T>Yes (if T: Send)NoYes (if mut)Heap allocation
Rc<T>NoYesNoReference counting
Arc<T>YesYesNoAtomic ref counting
RefCell<T>NoNoYes (runtime check)Borrow tracking
Mutex<T>YesVia ArcYes (lock)Lock overhead
RwLock<T>YesVia ArcYes (write lock)Lock overhead
Cow<'a, T>DependsNoClone on writePossible clone
Weak<T>Same as parentYesNoUpgrade 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 freed

Arc<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 immutably

Rc<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());  // 10

Cow<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 happened

Weak<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!)

Built with VitePress | Rust SOP Documentation