Type System Fundamentals

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