Skip to content

lint against float -> int casting #14356

@eric-seppanen

Description

@eric-seppanen

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 is 0
  • f32::NAN as i32 is 0
  • 1e20 as u32 is 4294967295. (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:

  1. All cases are already covered by the cast_sign_loss and cast_possible_truncation lints, which also catch integer casts that may fail. Maybe it's silly to treat floating point inputs as special.
  2. 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");

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lintArea: New lints

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions