We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Generics and Traits step 3 of 24
Trait bounds and why f64 is not Ord
A bare type parameter T can do almost nothing. You cannot add it, print it,
or compare it, because T might be any type and most types cannot do those
things. A trait bound is how you buy back exactly the capabilities you
need:
pub fn max_of<T: PartialOrd + Copy>(items: &[T]) -> Option<T>
Read T: PartialOrd + Copy as “whatever T turns out to be, it must
implement both PartialOrd and Copy“. Inside the body you may then use >
(from PartialOrd) and copy values around freely (from Copy) — and nothing
else.
This is where Rust differs sharply from C++ templates. A C++ template is type-checked once per instantiation, so a mistake surfaces as a wall of errors pointing deep inside a library. Rust type-checks the generic body once, against the bounds, before it has ever seen a caller. If the body compiles, every legal caller works. That guarantee is the reason Rust’s generic errors are readable at all.
Your task
pub fn max_of<T: PartialOrd + Copy>(items: &[T]) -> Option<T>
pub fn extremes(ints: Vec<i64>, floats: Vec<f64>) -> (Option<i64>, Option<f64>)
max_of returns the largest element, or None for an empty slice. extremes
calls it twice — once at T = i64, once at T = f64 — and returns both
answers. One generic function, two instantiations, no duplicated logic.
The lesson: PartialOrd vs Ord
The starter writes the bound as T: Ord + Copy, which is what most people
reach for. Compile it:
error[E0277]: the trait bound `f64: Ord` is not satisfied
|
11 | (max_of(&ints), max_of(&floats))
| ------ ^^^^^^^ the trait `Ord` is not implemented for `f64`
|
note: required by a bound in `max_of`
|
3 | pub fn max_of<T: Ord + Copy>(items: &[T]) -> Option<T> {
| ^^^ required by this bound in `max_of`
E0277 is the error you will meet most often in generic Rust, and it is
best read bottom-up. The first line states the failure; the note at the
end tells you which bound in which signature asked for it. That final note
is the actionable part. Train yourself to jump there first — this is a real
skill, not a footnote.
And the fact itself is worth internalising. f64 implements PartialOrd but
not Ord, because Ord promises a total order and floats have NaN:
NaN < 1.0, NaN > 1.0 and NaN == 1.0 are all false. There is no
consistent place to put it. PartialOrd promises only that a comparison may
return “these are not ordered”. Widening the bound from Ord to PartialOrd
is not a hack — it is an honest statement of what the algorithm needs.
(This problem’s inputs never contain NaN, so > behaves normally. When you
do need a total order on floats, f64::total_cmp is the standard answer.)
Two neighbouring errors
-
Drop
Copyfrom the bound and you get E0507, “cannot move out of … which is behind a shared reference”. That reads like a borrow error, but the cause is a missing bound: withoutCopy(orClone), taking a value out of the borrowed slice is a move you are not allowed to make. -
Use
>on aTwith no ordering bound at all and you get E0369, “binary operation>cannot be applied to typeT“. Operators are trait methods; no trait, no operator.
Remember the grade is compile + tests + clippy -D warnings.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.