We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Generics and Traits step 9 of 24
From, Into, and your first blanket impl with teeth
From and Into are the standard vocabulary for infallible conversion —
conversions that cannot fail, so there is no Result anywhere in sight.
The pair exists because of one impl in std:
impl<T, U> Into<U> for T
where
U: From<T>,
{ ... }
Read that carefully. It is a blanket impl: it covers every pair of types
T and U at once, on the condition that U: From<T>. So the moment you
write impl From<Celsius> for Fahrenheit, the whole Into side appears by
itself and celsius.into() starts working. You never write Into yourself.
Clippy enforces this, at default-on level:
error: an implementation of `From` is preferred since it gives you `Into<_>`
for free where the reverse isn't true [clippy::from_over_into]
The starter implements Into directly, so this is the first thing you will
see. The lint is the lesson, and it is not merely stylistic: only the From
direction feeds the ? operator’s automatic error conversion, which is why a
crate full of Into impls quietly cannot use ?.
Your task
pub struct Celsius(pub f64);
pub struct Fahrenheit(pub f64);
impl From<Celsius> for Fahrenheit { ... }
pub fn convert(temps: Vec<f64>) -> Vec<f64>
F = C × 9/5 + 32. convert wraps each input in a Celsius, converts with
.into(), and returns the inner f64. Keeping .into() in the body is the
point: it is the observable evidence that the blanket impl fired.
These are newtypes — single-field wrappers whose job is to make two
interchangeable f64s into two different types, so that passing a temperature
in the wrong unit becomes a compile error. Newtypes are cheap: the wrapper has
identical layout to the value inside and vanishes at runtime.
Three errors worth causing on purpose
Once your solution passes, try each of these in the editor. They are the reason this item is here.
1. Implement both. Add impl Into<Fahrenheit> for Celsius next to your
working From impl:
error[E0119]: conflicting implementations of trait `Into<Fahrenheit>`
for type `Celsius`
note: conflicting implementation in crate `core`
Your hand-written Into collides with the blanket one that already exists.
There is no way to “override” a blanket impl — Rust has no stable
specialisation.
2. Convert a type to itself. Write impl From<Celsius> for Celsius:
error[E0119]: conflicting implementations of trait `From<Celsius>`
for type `Celsius`
std contains impl<T> From<T> for T — the reflexive impl. Every type can
already convert to itself, so yours is a duplicate. This one genuinely
confuses people, because nothing in your file mentions the conflicting impl.
3. Implement a foreign trait for a foreign type. Try
impl From<Vec<u8>> for String:
error[E0117]: only traits defined in the current crate can be implemented
for types defined outside of the crate
That is the orphan rule, and it is the reason newtypes exist at all. It gets a problem of its own later in this track.
The asymmetry to remember
-
In an impl, write
From. You getIntofree and?conversion free. -
In a bound, write
Into:fn f<T: Into<Fahrenheit>>(t: T)accepts more callers thanwhere Fahrenheit: From<T>reads. Both are satisfied by the same set of types, but theIntospelling is the conventional one at a function boundary.
That asymmetry — From in impls, Into in bounds — looks arbitrary until you
have written both a few times. It is worth memorising as a rule now.
Remember the grade is compile + tests + clippy -D warnings.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.