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.