We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Generics and Traits step 21 of 24
Extension traits: adding methods to types you do not own
The previous problem made the orphan rule feel like a wall. This one makes it a tool.
The rule says: for impl Trait for Type, at least one of the two must be
local. The newtype escape makes the type local. The extension trait
pattern makes the trait local instead — and then you can implement it for
any foreign type you like, with no wrapper at all:
pub trait StatsExt { // local trait
fn mean(&self) -> f64;
fn median(&self) -> f64;
}
impl StatsExt for [f64] { } // foreign type — perfectly legal
Say the halves out loud before you write it: the trait is mine, the type is
std’s. That is the allowed direction. The reverse —
impl std::fmt::Display for [f64] — is E0117.
This is the pattern behind every use something::SomethingExt; line you have
seen in real Rust code. itertools::Itertools, rand::Rng,
futures::StreamExt are all local traits blanket- or specifically-implemented
for foreign types, and the use is what switches them on.
Your task
pub trait StatsExt { fn mean(&self) -> f64; fn median(&self) -> f64; }
impl StatsExt for [f64] { ... }
pub fn summarise(xs: Vec<f64>) -> (f64, f64)
-
mean— the arithmetic mean. Empty slice returns0.0. -
median— the middle value of the sorted data for odd lengths, the mean of the two middle values for even lengths. Empty slice returns0.0.
summarise returns (mean, median) and is written for you.
Implement it for [f64], not Vec<f64>
This is the detail that matters, and the starter forces it: summarise calls
xs.as_slice() first, so the receiver really is a &[f64]. If you write
impl StatsExt for Vec<f64> you get
error[E0599]: no method named `mean` found for reference `&[f64]`
Implementing on the slice is also strictly better. [f64] is the borrowed
form, so your methods become available on Vec<f64> (via deref), on
[f64; 8] (via unsizing), and on any sub-range like &xs[2..5]. Implementing
on Vec<f64> gets you exactly one of those three.
Sorting floats, correctly
median needs the data sorted, and slice::sort requires T: Ord, which
f64 is not — you saw why earlier in this track. Two idiomatic options:
sorted.sort_by(f64::total_cmp); // total order, no panic
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap()); // panics on NaN
Prefer total_cmp. It is a genuine total order (it files NaN at the ends
and distinguishes -0.0 from 0.0), it never panics, and it needs no
unwrap.
A related warning about comparators in general: if you hand a sort an inconsistent comparator, the documentation says it may panic — and may equally return normally with the elements in an unspecified order. Never write code that depends on the panic happening.
The invisible half of the pattern
In real multi-module code, an extension trait’s methods are only callable
where the trait is in scope. Forget the use and you get:
error[E0599]: no method named `mean` found for reference `&[f64]`
items from traits can only be used if the trait is in scope
help: trait `StatsExt` which provides `mean` is implemented but not in scope;
perhaps you want to import it
That is the classic symptom, and the help: line is the fix. In this
single-file harness everything is already in one scope, so the requirement
is real but invisible here. Remember it exists; it is the reason a crate
that offers extension traits documents the import on the first line of its
README.
It is also why clippy::wildcard_imports matters. use foo::*; pulls in
every extension trait a module happens to define, and each one joins method
resolution for every type it covers — which is exactly how you end up with a
surprise E0034.
Remember the grade is compile + tests + clippy -D warnings.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.