Deref Coercion
In Rust's type system, deref coercion automatically converts a reference &T (or &mut T) into &U (or &mut U) whenever T implements Deref<Target = U> (or DerefMut<Target = U>), allowing smart pointers and owning containers to act transparently like references to their inner data.
Transitive Resolution Chain (T -> U -> V)
When type-checking a coercion site or resolving the receiver of a . method call, the compiler repeatedly applies Deref::deref along a chain of Target types until the expected type is reached:
&Arc<Box<String>>
│ Arc<Box<String>>::deref() (Target = Box<String>)
▼
&Box<String>
│ Box<String>::deref() (Target = String)
▼
&String
│ String::deref() (Target = str)
▼
&str
use std::rc::Rc;
fn count_vowels(text: &str) -> usize {
text.chars()
.filter(|&c| matches!(c.to_ascii_lowercase(), 'a' | 'e' | 'i' | 'o' | 'u'))
.count()
}
fn main() {
let shared_boxed: Rc<Box<String>> = Rc::new(Box::new(String::from("Deref Coercion")));
// 3-step deref coercion chain: `&Rc<Box<String>>` -> `&Box<String>` -> `&String` -> `&str`:
let vowels = count_vowels(&shared_boxed);
println!("Vowels in '{}': {vowels}", *shared_boxed);
}
Vowels in 'Deref Coercion': 6
Interaction With Method Call Autoref and Autoderef
When you write receiver.method(), the compiler's method resolution algorithm alternates between autoderef (dereferencing pointers/wrappers) and autoref (borrowing T as &T or &mut T) until it finds an inherent or trait method matching method:
Candidate Receiver Chain for `vec.is_sorted()` (where `vec: Vec<i32>`):
Step 1 (T = Vec<i32>): try `Vec<i32>`, `&Vec<i32>`, `&mut Vec<i32>`
Step 2 (autoderef -> [i32]): try `[i32]`, `&[i32]` <── Matches `<[i32]>::is_sorted(&self)`!
Why Deref Coercion Does Not Apply to Generic Type Parameters
Deref coercion is a coercion between concrete reference types (&String $\rightarrow$ &str), not a subtyping relationship between String and str. If a generic function signature ties a type parameter T directly to &T, the compiler unifies T with the exact argument type before coercions run:
use std::ops::Deref;
// Accepts any `&str` via deref coercion:
fn concrete_str(s: &str) -> usize {
s.len()
}
// Generic function that explicitly allows Deref coercion via `Deref<Target = str>`:
fn generic_deref<S: Deref<Target = str>>(s: S) -> usize {
s.len() // Calls `str::len(&*s)` via deref coercion inside the body
}
fn main() {
let owned = String::from("hello");
println!("Concrete: {}", concrete_str(&owned));
println!("Generic: {}", generic_deref(&owned));
println!("Box<str>: {}", generic_deref(owned.into_boxed_str()));
}
Concrete: 5
Generic: 5
Box<str>: 5