We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Generics and Traits step 10 of 24
TryFrom, TryInto, and the type that cannot exist
From is for conversions that cannot fail. TryFrom is for the rest, and it
is the idiomatic home for validation logic:
impl TryFrom<i64> for Percentage {
type Error = PercentError;
fn try_from(value: i64) -> Result<Self, Self::Error> { ... }
}
Putting the check here rather than in a free fn validate has a real payoff:
because the associated Error type participates in From conversion, a
TryFrom slots straight into the ? operator in any function whose error
type can absorb yours.
Everything you learned about From/Into repeats one level up.
Implement TryFrom; never implement TryInto. std contains
impl<T, U> TryInto<U> for T
where
U: TryFrom<T>,
{ ... }
so .try_into() appears by itself. Once your TryFrom impl exists, writing
impl TryInto<Percentage> for i64 yourself is:
error[E0119]: conflicting implementations of trait `TryInto<Percentage>`
for type `i64`
= note: conflicting implementation in crate `core`:
- impl<T, U> TryInto<U> for T where U: TryFrom<T>;
Try it once, after your solution passes, so the shape of that error is familiar.
Your task
pub struct Percentage(pub u8);
impl TryFrom<i64> for Percentage { ... }
pub fn convert(vals: Vec<i64>) -> Vec<String>
A Percentage holds a u8 in 0..=100. For each input, convert produces:
| input | output |
|---|---|
0, 50, 100 |
0% 50% 100% |
101, 255 |
too large: 101, too large: 255 |
-1, 256, i64::MAX |
not a byte |
So there are two distinct failures and the error type must tell them
apart. Build them from u8::try_from(value): if that fails the value is not
even a byte, and its error type is std::num::TryFromIntError. If it succeeds
but exceeds 100, that is your own range failure.
convert must go through .try_into() and render the error with
.to_string(), which means your error type needs Display.
The starter’s gate: Infallible
The starter declares type Error = Infallible and clamps instead of
validating. Clippy refuses it, at default-on level:
error: infallible TryFrom impl; consider implementing From instead
|
6 | type Error = Infallible;
| ---------- infallible error type
A TryFrom that never fails is a From wearing a costume — every caller now
has to unwrap a Result that is always Ok. Either the conversion can fail,
in which case say how, or it cannot, in which case implement From.
std::convert::Infallible is worth a moment on its own. It is an enum with
no variants, so no value of that type can ever be constructed. The type
system tracks impossibility, and two consequences follow that surprise people:
match e {} // legal: an empty match on an uninhabited type
size_of::<Result<u8, Infallible>>() == 1
The first compiles because the compiler can see there is no case to handle.
The second says the error variant costs nothing — a Result whose error
cannot exist is exactly as large as the success value. The starter uses the
empty match; it is real Rust, not a trick.
One more lint in this neighbourhood
clippy::unnecessary_fallible_conversions is also default-on. It rejects
i64::try_from(x) where x: u8, because that conversion cannot fail and
i64::from(x) is exact. Reach for try_from when the conversion is genuinely
narrowing, and from when it is widening.
Remember the grade is compile + tests + clippy -D warnings.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.