Smart Pointer Dereferencing
Smart pointer dereferencing is the mechanism by which a smart pointer wrapper (Box<T>, Rc<T>, Arc<T>, Ref<'_, T>, MutexGuard<'_, T>) provides transparent access to its inner target value T through the unary * operator and automatic deref coercion (Deref and DerefMut).
The Deref and DerefMut Traits
Defined in std::ops, Deref and DerefMut overload the dereference operator *:
pub trait Deref {
type Target: ?Sized;
fn deref(&self) -> &Self::Target;
}
pub trait DerefMut: Deref {
fn deref_mut(&mut self) -> &mut Self::Target;
}
When you write *smart_ptr in an immutable context, the compiler rewrites the expression to *(Deref::deref(&smart_ptr)). In a mutable context (*smart_ptr = new_val), the compiler rewrites it to *(DerefMut::deref_mut(&mut smart_ptr)).
use std::ops::{Deref, DerefMut};
struct TrackedBox<T> {
value: T,
writes: usize,
}
impl<T> TrackedBox<T> {
fn new(value: T) -> Self {
Self { value, writes: 0 }
}
}
impl<T> Deref for TrackedBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T> DerefMut for TrackedBox<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.writes += 1;
&mut self.value
}
}
fn main() {
let mut tracked = TrackedBox::new(String::from("initial"));
// Calls `DerefMut::deref_mut` to mutate the inner String:
tracked.push_str(" + updated");
*tracked = tracked.to_uppercase();
// Calls `Deref::deref` to read the inner String as `&str`:
let as_str: &str = &tracked;
println!("value: {:?}, writes: {}", as_str, tracked.writes);
}
value: "INITIAL + UPDATED", writes: 2
Why Smart Pointer Associated Functions Take &Rc<T> Instead of &self
Because Deref automatically forwards .method() calls on a smart pointer to its inner Target type T, adding fn clone(&self) or fn downgrade(&self) as regular self methods on Box<T>, Rc<T>, or Arc<T> could collide with a method of the same name defined on the inner type T.
To prevent method-name collisions with T, smart pointer types define their own operations as associated functions called with path syntax (Rc::clone(&rc), Rc::downgrade(&rc), Rc::strong_count(&rc), Arc::ptr_eq(&a, &b), Box::into_raw(b)).
use std::rc::Rc;
struct Document {
title: String,
}
impl Document {
fn strong_count(&self) -> &'static str {
"Document::strong_count method on inner type"
}
}
fn main() {
let doc = Rc::new(Document {
title: String::from("Spec"),
});
// Method syntax resolves via `Deref` to `Document::strong_count`:
println!("doc.strong_count(): {}", doc.strong_count());
// Associated function syntax calls `Rc::strong_count`:
println!("Rc::strong_count(&doc): {} ({})", Rc::strong_count(&doc), doc.title);
}
doc.strong_count(): Document::strong_count method on inner type
Rc::strong_count(&doc): 1 (Spec)
Multi-Layer Smart Pointer Dereferencing
When smart pointers are nested (such as Arc<Mutex<Vec<u8>>>), dereferencing steps through each layer according to its synchronization or borrowing API:
Arc<Mutex<Vec<u8>>>$\xrightarrow{\text{Deref}}$Mutex<Vec<u8>>(automatic on.lock()).mutex.lock().unwrap()$\rightarrow$MutexGuard<'_, Vec<u8>>.MutexGuard<'_, Vec<u8>>$\xrightarrow{\text{Deref / DerefMut}}$Vec<u8>$\xrightarrow{\text{Deref}}$[u8].