A smart pointer is a data structure that acts like a pointer but adds extra capabilities. Box<T> puts a value on the heap with ordinary single ownership, useful for recursive types or large data you don't want to copy. Rc<T> (reference counted) allows multiple owners of the same data by tracking a count of active references, freeing the data when the count hits zero — single-threaded only; the thread-safe equivalent is Arc<T>. RefCell<T> enforces the borrowing rules at runtime instead of compile time, allowing mutation through a reference that looks immutable, at the cost of a possible runtime panic instead of a compile error if the rules are violated.
Box<T>: heap allocation, single owner
Box is needed for recursive types, since Rust must know a type's size at compile time — a type that contains itself directly would be infinitely large, but a Box (a fixed-size pointer) breaks the cycle.
enum List {
Cons(i32, Box<List>), // Box breaks the infinite-size cycle
Nil,
}
use List::{Cons, Nil};
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));Rc<T>: multiple owners via reference counting
Rc::clone doesn't deep-copy the data — it increments a reference count and returns a new pointer to the same heap allocation. The data is freed only when the count reaches zero.
use std::rc::Rc;
let a = Rc::new(String::from("shared data"));
println!("{}", Rc::strong_count(&a)); // 1
let b = Rc::clone(&a); // increments the count, doesn't copy the string
println!("{}", Rc::strong_count(&a)); // 2
drop(b);
println!("{}", Rc::strong_count(&a)); // 1 againRefCell<T>: interior mutability, checked at runtime
RefCell lets you mutate data even through what the type system sees as an immutable reference, by moving the borrow-checking rules to runtime — .borrow_mut() panics if another borrow is already active, exactly the rule the compiler would otherwise enforce statically.
use std::cell::RefCell;
let value = RefCell::new(5);
*value.borrow_mut() += 1; // mutate through a "shared" RefCell
println!("{}", value.borrow()); // 6
// let a = value.borrow_mut();
// let b = value.borrow_mut(); // RUNTIME PANIC: already mutably borrowed