Smart Pointers
Wiki Index
- These are custom reference types that can store additional data.
- Any custom type that implements
Deref
andDrop
is a smart pointer. - Rust walks the tree of potential types that implement
Deref
to allow for automatic dereferencing/deref coercion to a given target type:fn hello(name: &str) { println!("Hello, {}!", name); } fn main() { // Box<T> implements `Deref` and returns a `&T` (in this case a `&String`) // ... which in turn implements `Deref` and returns a `&str` // ... which `hello` accepts let m = Box::new(String::from("Rust")); hello(&m); }
- Not sure what Rust does if there are multiple paths to the target reference type, though.
- The Rust compiler knows about three builtin smart pointer types.
Box<T>
- A container pointing to data on the heap
- The box owns the data on the heap, and allows mutable/immutable borrows.
- Different from a regular reference in that it allows boxing primitives.
- And allows for indirection in recursive type definitions:
// Fails because this definition recurses indefinitely // when Rust attempts to figure out how much memory // a `List<T>` needs. enum List<T> { Cons(T, List<T>), Nil } // This works enum List<T> { Cons(T, Box<List<T>>), Nil }
Rc<T>
(Reference Counted Pointer)
- Use this if you want multiple owners for something (and having one owner + immutable references is untenable).
- This is a reference counting container, (effectively) allowing multiple owners for a single allocation.
- Uses its
Drop
trait to deallocate when it’s reference count drops to 0. - Supports weak references via
downgrade
. - Use
Rc::clone
to obtain a new (owning) reference to the allocation. - This pattern is vulnerable to memory leaks.
- An
Rc<T>
derefs to an immutable reference to the underlying data, but can provide a mutable reference (assuming it’s the only reference present) too viaget_mut
. Arc<T>
is a thread-safe version, and can be used withMutex
to allow multiple threads to own a piece of data:// We're going to share this empty vector across threaeds let data = Arc::new(Mutex::new(vec![])); // Start 16 threads, give each one ownership of the mutex // guarding the vector by cloning the `Arc`. Each thread // then calls `lock()`, which only unlocks for one caller // at a time, and returns a mutable reference to the vector // held by the mutex. The mutex is unlocked when `v` goes // out of scope. let threads: Vec<_> = (0..16).map(|i| { let data = Arc::clone(&data); thread::spawn(move || { let mut v = data.lock().unwrap(); v.push(i); }) }).collect(); // Wait until all 16 threads are done. for t in threads { t.join().unwrap(); } // Mutex { data: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] } println!("{:?}", data);
RefCell<T>
- Use this if you want to mutate something (non-public scratch space, for example) behind an immutable reference.
- Immutable container to a mutable piece of the heap.
- Single owner, allows any number of borrows, but unlike regular allocations, the borrow rules are enforced at runtime, and rust panics if there’s a violation.
- Deallocates the data on the heap when the owner goes out of scope.
- Doesn’t implement
Deref
(because this is a Ref Cell, not a reference); useborrow
/borrow_mut
instead.