We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ground Rules: Values, Types, Control Flow step 11 of 24
From, Into, TryFrom: conversions that cannot lie
Convert each i64 to a u8, and say None when it does not fit.
pub fn to_bytes(values: &[i64]) -> Vec<Option<u8>>
[0, 255, 256, -1] becomes [Some(0), Some(255), None, None], which the
harness prints as [0, 255, null, null].
The starter compiles and passes clippy. It returns [0, 255, 0, 255], which is
what the previous item’s as does — and putting the two answers side by side
is the lesson.
Three traits, one idea
trait From<T> { fn from(value: T) -> Self; }
trait TryFrom<T> { type Error; fn try_from(value: T) -> Result<Self, Self::Error>; }
-
Fromis for conversions that always succeed and never lose information.i64::from(u8)exists because every byte is ani64.u8::from(i64)does not exist, and cannot, because mosti64s are not bytes. -
Intois the same conversion read from the other end. You never implement it: there is a blanket impl saying that ifU: From<T>thenT: Into<U>. Writelet x: i64 = byte.into();when the target type is obvious from context, andi64::from(byte)when it is not. -
TryFromis for conversions that can fail, and it returns aResultcarrying an error that explains why.u8::try_from(300i64)isErr(TryFromIntError(())).
The standard library implements these exhaustively across the integer types, and the presence or absence of an impl is a machine-checked statement about whether a conversion is lossless. That is a remarkable amount of documentation encoded as types.
Result and .ok()
try_from gives you Result<u8, TryFromIntError> — either Ok(value) or
Err(why). This problem does not care why it failed, only that it did, so
throw the error away:
u8::try_from(v).ok() // Result<u8, E> -> Option<u8>
Result::ok maps Ok(x) to Some(x) and Err(_) to None. There is a
matching Option::ok_or going the other way when you need to invent an error.
Track 6 covers error handling properly; for now, .ok() is the one-word bridge.
The teaching line: “TryFrom by default, as on purpose”
Put the two implementations next to each other:
| input |
as u8 |
u8::try_from(...).ok() |
|---|---|---|
0 |
0 |
Some(0) |
255 |
255 |
Some(255) |
256 |
0 |
None |
-1 |
255 |
None |
The as column contains two answers that are indistinguishable from correct
ones. Nothing in the type, the signature or the test output says that 256
became 0 because of a bug rather than because someone meant it.
Note the framing. It is not “never use as“. as is right for a
deliberate truncation (the odometer in item 1.5), for a documented saturating
float conversion, and in tight numeric code where the range is already
established. The community genuinely argues about where the line sits. What is
not contested is the default: if you have not thought about the failure case,
you want the version that makes you.
Note on usize
usize is 8 bytes on the machines that grade this, so usize::try_from(i64)
succeeds for every non-negative input here. It is still the right call, because
the same source compiled for a 32-bit target would behave differently and
try_from is the version that stays correct.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.