Deref and DerefMut Traits
Deref and DerefMut Traits
Level 14 — Rust Overloads the
*dereference operator and enables deref coercions — automatically convertingBox<T>to&T,Stringto&str, andVec<T>to&[T]in many contexts.
1. Prerequisites
Deref/DerefMutTraits — Deref traits.
2. Term Category
Rust Standard Trait (immutable dereferencing operator overload): std::ops::Deref and DerefMut for customized dereferencing operator behavior (*).
3. Explanation
(1) Design Motivation — "Why did we design this?"
Custom container types like Box<T>, Rc<T>, or Arc<T> wrap inner values in heap allocations. Without Deref, accessing methods or fields on the inner value T would require writing verbose wrapper method delegates for every single method on T.
Implementing Deref (type Target = T; fn deref(&self) -> &T) allows custom smart pointers to overload the unary dereference operator *ptr and automatically expose all underlying methods of T.
(2) Reality Metaphor
A transparent protective sleeve over a smartphone: tapping the outer screen sleeve directly passes touch events through to the phone display underneath.
(3) Rust Code Examples
Short Snippet
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T { &self.0 }
}
Fuller Example
use std::ops::{Deref, DerefMut};
pub struct MeasuredBuffer<T> {
data: Vec<T>,
pub access_count: usize,
}
impl<T> MeasuredBuffer<T> {
pub fn new(data: Vec<T>) -> Self {
Self { data, access_count: 0 }
}
}
impl<T> Deref for MeasuredBuffer<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.data
}
}
fn main() {
let buf = MeasuredBuffer::new(vec![1, 2, 3]);
// Method call transparently forwarded to Vec<T> via Deref!
assert_eq!(buf.len(), 3);
}
4. Common Mistakes & Pitfalls
Mistake 1: Abusing Deref for Struct Inheritance Simulation
The mistake: Implementing Deref on a domain struct (e.g. User) targeting another struct (Account) to fake OOP class inheritance.
Why it is wrong: Deref is designed specifically for smart pointers. Abusing it for domain structs causes confusing method resolution bugs and violates idiomatic Rust composition rules.
Incorrect:
impl Deref for User { type Target = Account; ... } // Antipattern!
Fix:
Use explicit fields (`user.account`) or delegation traits instead of abusing Deref!
Mistake 2: Forgetting DerefMut for Mutable Access
The mistake: Implementing Deref without DerefMut and expecting *ptr = new_val or mutable method calls (ptr.push()) to work.
Why it is wrong: Deref only grants immutable &Target references. Mutable dereferencing requires implementing DerefMut.
Incorrect:
let mut my_box = MyBox(vec![1]); my_box.push(2); // Error without DerefMut!
Fix:
impl<T> DerefMut for MyBox<T> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } }
Mistake 3: Creating Recursive Infinite Loops in deref Implementation
The mistake: Invoking *self or calling a method on self inside deref().
Why it is wrong: Triggers infinite recursion stack overflow during execution.
Incorrect:
impl Deref for Wrapper { type Target = Inner; fn deref(&self) -> &Inner { &*self } } // Stack overflow!
Fix:
Return reference to underlying field: fn deref(&self) -> &Inner { &self.inner }
5. Practice Exercises
Exercise 1: Custom Smart Pointer Container with Deref and DerefMut
Scenario: Implement a smart pointer TrackedBox<T> counting read and write access counts while implementing Deref and DerefMut.
Requirements:
- Define
TrackedBox<T>wrappingT. - Implement
Deref<Target = T>andDerefMut. - Write unit tests for dereferencing and mutable methods.
Answer
Implementation
use std::ops::{Deref, DerefMut};
pub struct TrackedBox<T> {
value: T,
pub reads: usize,
pub writes: usize,
}
impl<T> TrackedBox<T> {
pub fn new(value: T) -> Self {
Self { value, reads: 0, 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 {
&mut self.value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tracked_box_deref() {
let mut boxed = TrackedBox::new(vec![10, 20]);
// Immutable deref method call on Vec<i32>
assert_eq!(boxed.len(), 2);
// Mutable deref method call on Vec<i32>
boxed.push(30);
assert_eq!(boxed.len(), 3);
}
}
Technical Explanation
- Implementing
DerefandDerefMutexposes allVec<T>methods transparently onTrackedBox<T>. - Overloads
*boxedoperator.
Exercise 2: Lazy Initialization Singleton Guard with Deref
Scenario: Implement a thread-safe lazy resource guard implementing Deref to expose initialized database configuration.
Requirements:
- Define
LazyGuard<T>. - Implement
Deref.
Answer
Implementation
use std::ops::Deref;
pub struct LazyConfig {
pub host: String,
pub port: u16,
}
pub struct ConfigGuard {
config: LazyConfig,
}
impl ConfigGuard {
pub fn load() -> Self {
Self {
config: LazyConfig {
host: "localhost".into(),
port: 5432,
},
}
}
}
impl Deref for ConfigGuard {
type Target = LazyConfig;
fn deref(&self) -> &Self::Target {
&self.config
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_guard_deref() {
let guard = ConfigGuard::load();
assert_eq!(guard.host, "localhost");
assert_eq!(guard.port, 5432);
}
}
Technical Explanation
ConfigGuardtransparently forwards field accesses to innerLazyConfig.
Exercise 3: String Alias Wrapper Smart Pointer
Scenario: Build a validated EmailAddress newtype smart pointer delegating str methods via Deref.
Requirements:
- Define
EmailAddress(String). - Implement
Deref<Target = str>.
Answer
Implementation
use std::ops::Deref;
pub struct EmailAddress(String);
impl EmailAddress {
pub fn parse(s: &str) -> Result<Self, &'static str> {
if s.contains('@') {
Ok(EmailAddress(s.to_string()))
} else {
Err("Invalid email format")
}
}
}
impl Deref for EmailAddress {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_email_deref() {
let email = EmailAddress::parse("user@example.com").unwrap();
assert!(email.ends_with("@example.com")); // Directly uses str::ends_with!
}
}
Technical Explanation
- Newtype smart pointer validating invariants on construction while exposing
strmethods viaDeref.
6. Related Terms
- Newtype Pattern —
Deref/DerefMutTraits — Deref/DerefMut traits.
7. Key Takeaways
- Overloads the dereference operator
*ptr. - Required for smart pointer implementations (
Box,Rc,Arc,RefCellguards). - Implement
DerefMutfor mutable dereferencing. - Do not abuse
Dereffor domain struct inheritance.