We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ground Rules: Values, Types, Control Flow step 10 of 24
Casting with as: truncation, sign reinterpretation, saturation
Narrow each value to a u8 and widen it straight back to i64. Report what
survived the round trip.
pub fn cast_report(values: &[i64]) -> Vec<i64>
300 comes back as 44. -1 comes back as 255. 0 stays 0. 256 comes
back as 0.
The starter fails clippy on a cast that does nothing, which is a small joke at the expense of the operator this whole item is about.
as never fails, and that is the problem
as is the most dangerous operator a Rust beginner reaches for, because it
always compiles, never returns an error, never panics, and never warns by
default. It is the one place in the language where a value can silently become
a different value.
Integer to narrower integer: keep the low bits.
300i64 as u8 // 44 — 300 - 256
256i64 as u8 // 0
-1i64 as u8 // 255 — two's complement, all ones
Integer to wider integer: sign-extend if the source was signed.
-1i8 as i32 // -1 (sign-extended)
-1i8 as u32 // 4294967295 (reinterpreted)
Same width, different sign: reinterpret the bits, do not change them.
255u8 as i8 // -127 - 1 = -128? no: -1. The byte 0xFF read as signed.
None of this is undefined. All of it is a place where your number quietly became a different number.
Float to integer saturates — since 1.45
This is the corner people get wrong from old memory, so be precise about it. As
of Rust 1.45, f64 as i32 saturates, and it is fully defined:
1e10_f64 as i32 // 2147483647 = i32::MAX (saturates, does not wrap)
-3.99_f64 as i32 // -3 (truncates toward zero, does not round)
f64::NAN as i32 // 0
-1e10_f64 as i32 // -2147483648 = i32::MIN
Before 1.45 the out-of-range cases were undefined behaviour, and a great deal
of internet folklore still says so. It is not true on any compiler you will
meet today. Note also that the fractional case truncates toward zero — -3.99
becomes -3, not -4.
What clippy does and does not tell you
Here is the uncomfortable part. The two lints that would warn you about the dangerous cases —
-
cast_possible_truncation— “castingi64tou8may truncate”; -
cast_sign_loss— “castingi64tou8may lose the sign”;
— are both in the pedantic group, which is off by default. So out of the box, clippy teaches you nothing whatsoever about the single most dangerous operator in the language. That is worth knowing about the tool: it is opinionated, its defaults are tuned for low noise, and low noise is not the same as high safety.
What is on by default is unnecessary_cast, which fires when a cast has
no effect at all — v as i64 where v is already i64. That is the starter’s
bug, and it is a real one: a redundant cast tells the reader a conversion is
happening when none is.
Writing it
Narrow with as u8, then widen back. For the widening step, prefer
i64::from(byte) over byte as i64 — the From impl exists precisely because
that direction is always exact, and using it documents that no information can
be lost. Item 1.11 turns this preference into a rule.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.