We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Generics and Traits step 10 of 24
TryFrom and TryInto: validate a percentage
Validate integer percentages using TryFrom, then format each conversion result.
pub struct Percentage(pub u8);
pub fn convert(vals: Vec<i64>) -> Vec<String>
Implement TryFrom<i64> for Percentage and an error type with two variants:
NotAByte(std::num::TryFromIntError) and TooLarge(u8).
Check the byte range before the percentage range
First call u8::try_from(value). If that fails, return NotAByte containing
the original conversion error. If it succeeds, accept values through 100
inclusive and return TooLarge for larger bytes.
In convert, use .try_into() for each input. Format a successful percentage
as "<value>%", and format an error with .to_string() using these exact messages:
| Input range | Output |
|---|---|
0..=100 |
The value followed by %, such as "50%". |
101..=255 |
"too large: <value>", such as "too large: 101". |
| Below 0 or above 255 | "not a byte". |
Preserve input order and duplicates. Return one string per input, including
errors; an empty input returns an empty vector. Do not clamp, wrap or skip
invalid values. In particular, 256 produces "not a byte", not "too large: 256".
input = [0, 100, 101, 255, 256, -1]
output = ["0%", "100%", "too large: 101", "too large: 255",
"not a byte", "not a byte"]
The conversion enforces 0..=100. The supplied public tuple field does not
enforce that invariant by itself: code can still construct Percentage(200)
directly. An API that must prevent that would keep its field private and
expose checked constructors.
Implement TryFrom to get TryInto
TryFrom provides a standard interface for a conversion that may fail.
Its associated Error type identifies the failure returned in Result.
Implementing TryFrom<i64> for Percentage also supplies
TryInto<Percentage> for i64 through the standard library's blanket implementation.
Adding that same TryInto implementation yourself would conflict.
Prefer implementing TryFrom for this task. That is a convention supported
by the blanket implementation, not a rule that directly implementing
TryInto is always illegal.
The target type sometimes needs to be stated at the call site:
let parsed: Result<Percentage, PercentError> = value.try_into();
Give errors a useful display and source
Implement Display for the two exact messages above. This supplies the
formatting used by .to_string().
Also implement std::error::Error. Its source() should return the contained
TryFromIntError for NotAByte, and None for TooLarge. The source preserves
the underlying cause even though the displayed message is short. Deriving
Debug provides the other formatting trait required by Error.
The conversion can start with:
let byte = u8::try_from(value).map_err(PercentError::NotAByte)?;
The enum variant constructor wraps the integer-conversion error. ? then
returns that error early, or yields the byte for the second check. This
propagation works for other Result-returning functions too; it is not a
special capability restricted to conversion traits.
Why the starter's Infallible error is unsuitable
The starter clamps every input into 0..=100, always returns Ok, and declares
Infallible as its error. It therefore violates the required behavior for
invalid inputs. Clippy also flags its explicit infallible TryFrom implementation.
Infallible has no variants, so a valid value of that type cannot be produced.
An empty match can express that an error branch is impossible. Here is a
complete example with an actually infallible conversion:
use std::convert::Infallible;
fn unwrap_infallible<T>(result: Result<T, Infallible>) -> T {
match result {
Ok(value) => value,
Err(error) => match error {},
}
}
fn main() {
let byte = 7_u8;
let number: i64 = i64::from(byte);
assert_eq!(unwrap_infallible(Ok(number)), 7);
}
Use From when the conversion has an appropriate infallible implementation,
such as u8 to i64. Use TryFrom when you need a checked conversion, such
as i64 to u8. Do not choose solely by type size: conversions also have
semantic requirements, such as this task's percentage limit.
A passing submission must compile, pass the tests, and pass
clippy -D warnings. The trait and error-source design above is part of the
exercise; output checks alone cannot establish every implementation detail.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.