6.8 Smart Pointers and Heap Allocation
Rust includes various smart pointers that safely manage heap allocations. We will explore each in depth in later chapters. Below is a brief overview.
6.8.1 Box<T>
: Simple Heap Allocation
Box<T>
places data on the heap, storing only a pointer on the stack. When the Box<T>
is dropped, the heap allocation is freed:
fn main() { let b = Box::new(5); println!("b = {}", b); } // `b` is dropped, and its heap data is freed
6.8.2 Recursive Types with Box<T>
Box<T>
frequently appears in recursive data structures:
enum List { Cons(i32, Box<List>), Nil, } fn main() { use List::{Cons, Nil}; let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))))); }
6.8.3 Rc<T>
: Reference Counting for Single-Threaded Use
Rc<T>
(reference count) allows multiple “owners” of the same data in single-threaded environments:
use std::rc::Rc; fn main() { let a = Rc::new(String::from("hello")); let b = Rc::clone(&a); let c = Rc::clone(&a); println!("{}, {}, {}", a, b, c); }
Rc::clone()
does not create a deep copy; instead, it increments the reference count of the shared data. When the last Rc<T>
is dropped, the data is freed.
6.8.4 Arc<T>
: Atomic Reference Counting for Threads
Arc<T>
is a thread-safe version of Rc<T>
that uses atomic operations for the reference count:
use std::sync::Arc; use std::thread; fn main() { let a = Arc::new(String::from("hello")); let a1 = Arc::clone(&a); let handle = thread::spawn(move || { println!("{}", a1); }); println!("{}", a); handle.join().unwrap(); }
6.8.5 RefCell<T>
and Interior Mutability
RefCell<T>
permits mutation through an immutable reference (interior mutability) with runtime borrow checks:
use std::cell::RefCell; fn main() { let data = RefCell::new(5); { let mut v = data.borrow_mut(); *v += 1; } println!("{}", data.borrow()); }
Combining Rc<T>
and RefCell<T>
allows multiple owners to mutate shared data in single-threaded code.