Recipe
Implement a foreign trait for a foreign type (via a newtype)
Use when: rustc reports E0117 because you want a standard library (or other crate's) trait on a type you didn't define
Goal: the behaviour you wanted, on a local wrapper type the orphan rule allows
Taught in Coherence and the orphan rule
Quick recipe
-
1
Declare a newtype around the foreign type:
struct Readings(Vec<f64>);.Example
The failing program has
impl fmt::Display for Vec<f64>. Add one line above it:struct Readings(Vec<f64>);. The impl hasn't moved yet, so rustc still reports E0117 at this point.Why: the wrapper is defined in your crate, so it's local, and a foreign trait may be implemented for a local type.
-
2
Move the impl to the wrapper,
impl fmt::Display for Readings, and reach the inner value asself.0.Example
The header becomes
impl fmt::Display for Readings. Inside,selfis now aReadings, soself.len()becomesself.0.len():write!(f, "{} readings", self.0.len()). The E0117 is gone.Why: the body is the same logic as before; only the type it's attached to has changed.
-
3
Where the program made a
Vec<f64>, make aReadings(vec![...])instead, and pass that to the code that uses the trait.Example
let temps = vec![18.5, 19.0, 21.25];becomeslet temps = Readings(vec![18.5, 19.0, 21.25]);. The lineprintln!("{temps}")stays as it was, and now prints3 readings.Why: the trait belongs to
Readings, not toVec<f64>, so it only applies to values of the wrapper type. -
4
If callers need a few of the inner type's methods, add small methods to the wrapper (
fn len(&self) -> usize { self.0.len() }), or use.0.Example
A caller wants the first reading.
temps.0[0]gives18.5. If callers often want the count, addimpl Readings { fn len(&self) -> usize { self.0.len() } }, andtemps.len()returns 3. Methods you don't add, likepush, stay out of reach unless someone goes through.0.Why: the wrapper is a real type, so you choose which operations it offers.
Common mistakes
- Declaring the wrapper but leaving the impl on the foreign type (still E0117).
- Forgetting to change where values are created, so the code still holds a plain
Vec. - Wrapping only the element type (
Vec<MyType>): the outerVecis still foreign.