-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
What it does
Using the as
keyword to cast between numeric types is hazardous, and is already covered by cast_sign_loss
, cast_possible_truncation
, and cast_possible_wrap
.
Casting between integer types, though it may not be wise, at least has easy to remember semantics: when casting to a smaller type, it truncates, and when casting between signed/unsigned, it reinterprets the bits (possibly leading to under/overflow).
(reference)
Casting from floating point to integer has different behavior, comes with additional hazards, and needs extra weird semantics to cover special cases:
- NaNs are converted to 0.
- Out-of-range inputs (including +/- infinity) are saturated to the destination type's min/max values.
- Fractional numbers are rounded toward zero.
This produces some surprising results:
-1.0 as u32
is0
f32::NAN as i32
is0
1e20 as u32
is4294967295
. (value is shrunk by 10 orders of magnitude)
This leaves me wondering if there should be a clippy lint that only targets as
casting from floating-point to integer.
Advantage
This might be helpful for codebases where integer->integer casts are just too common to warn against.
Drawbacks
Arguments against such a lint include:
- All cases are already covered by the
cast_sign_loss
andcast_possible_truncation
lints, which also catch integer casts that may fail. Maybe it's silly to treat floating point inputs as special. - There isn't one obvious fix to suggest. The standard library doesn't offer fallible conversions for NaN and out-of-range inputs. There are crates that can help:
num_traits::NumCast
has fallible conversions;conv2
has fallible conversions plus saturating, wrapping, rounding modes, etc.
Example
let x = some_float as u32;
Could be written as:
use conv2::ConvUtil;
let x = some_float.approx_as::<u32>().expect("input out of range");
or
use num_traits::NumCast;
let x: u32 = NumCast::from(x).expect("input out of range");