Deref and Wrapper Ergonomics
Make wrappers feel native.
Deref and Wrapper Ergonomics is a free Learn Rust Coding lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Learn Rust Coding learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
The Ergonomics Problem
Wrapping a type in a newtype gains safety but loses convenience: suddenly you must write wrapper.0.method() everywhere and reimplement methods by hand.
The Deref trait can restore much of that ergonomics by letting the wrapper behave like the value it holds.
What Deref Does
Deref defines what *value produces and powers the . operator. When you call a method that the wrapper lacks, the compiler tries again on the deref target.
This automatic step is called deref coercion.
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T { &self.0 }
}Deref Coercion in Action
Once Deref is implemented, calling inner methods on the wrapper just works. The compiler inserts .deref() for you until the method is found.
use std::ops::Deref;
struct Name(String);
impl Deref for Name {
type Target = String;
fn deref(&self) -> &String { &self.0 }
}
fn main() {
let n = Name("rustacean".to_string());
println!("len = {}", n.len()); // String::len via deref
}DerefMut for Mutable Access
Deref only gives shared access. To call methods that need &mut self through the wrapper, also implement DerefMut, which returns a mutable reference to the inner value.
use std::ops::{Deref, DerefMut};
struct Stack(Vec<i32>);
impl Deref for Stack {
type Target = Vec<i32>;
fn deref(&self) -> &Vec<i32> { &self.0 }
}
impl DerefMut for Stack {
fn deref_mut(&mut self) -> &mut Vec<i32> { &mut self.0 }
}Coercion at Function Boundaries
Deref coercion also applies to arguments. A &Name coerces to &String, which itself coerces to &str, so you can pass the wrapper straight into functions expecting the inner reference.
fn greet(who: &str) {
println!("hi {}", who);
}
// greet(&name) works: &Name -> &String -> &strA Runnable Coercion Example
This program passes a wrapper reference where a string slice is expected. The chained deref coercions happen silently at the call site.
use std::ops::Deref;
struct Tag(String);
impl Deref for Tag {
type Target = String;
fn deref(&self) -> &String { &self.0 }
}
fn shout(s: &str) { println!("{}!", s.to_uppercase()); }
fn main() {
let t = Tag("ship".to_string());
shout(&t);
}Do Not Abuse Deref
The Rust API guidelines warn against implementing Deref for types that are not genuine smart pointers. Overusing it makes the wrapper's available methods unpredictable and surprises readers.
If the newtype exists to restrict an API, blanket Deref can leak exactly the methods you meant to hide.
AsRef as a Targeted Alternative
When you only need explicit conversion rather than automatic coercion, implement AsRef. Callers write .as_ref() deliberately, which keeps the wrapper's own surface clear.
struct Path(String);
impl AsRef<str> for Path {
fn as_ref(&self) -> &str { &self.0 }
}Forwarding Selected Methods
For a restrictive wrapper, prefer hand-writing just the methods you want to expose. This keeps full control over the API instead of leaking everything through Deref.
struct Counter(Vec<u8>);
impl Counter {
fn len(&self) -> usize { self.0.len() }
fn push(&mut self, b: u8) { self.0.push(b); }
}Smart Pointers Use Deref
Standard smart pointers like Box, Rc, and Arc all implement Deref. That is why you can call inner methods on them transparently. This is the legitimate, intended use of the trait.
use std::rc::Rc;
fn main() {
let shared = Rc::new(String::from("data"));
// Deref lets us call String methods directly
println!("len = {}", shared.len());
}Choosing Your Ergonomics Strategy
Pick based on intent. Use Deref for true smart-pointer wrappers, AsRef for explicit borrowed conversions, and manual forwarding when a newtype must restrict its inner type's API.
The wrong choice either hides too much or leaks too much.
Quick Check
Decide which wrapper deserves a Deref implementation.
Recap
Deref and deref coercion restore ergonomics to wrappers by letting the . operator and reference arguments fall through to the inner value; DerefMut adds mutable access.
Reserve Deref for true smart pointers. For restrictive newtypes, use AsRef for explicit conversions or forward only the methods you choose to expose.
Frequently asked questions
Is the “Deref and Wrapper Ergonomics” lesson free?
Yes — the full text of “Deref and Wrapper Ergonomics” is free to read here on the web, and the Learn Rust Coding course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Learn Rust Coding course, upgrade to CoddyKit PRO.
What will I learn in “Deref and Wrapper Ergonomics”?
Make wrappers feel native. You practise Learn Rust Coding with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Learn Rust Coding?
No prior experience is required. Learn Rust Coding on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Deref and Wrapper Ergonomics” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Learn Rust Coding lesson?
Yes. Every Learn Rust Coding lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- The Builder Pattern
- The Newtype Pattern
- Type-State Builders
- Deref and Wrapper Ergonomics