There is currently a discussion about the dangers of numeric casts with as
. This has come up several times before, and I think it's time to fix it.
Casting between numeric types isn't unsafe, but it is very likely to cause bugs, and when used in unsafe code, it can cause UB down the line.
Integer casts are similar to integer arithmetic. By default, +
, -
and *
operations wrap on overflow in release builds, which is equivalent to doing the calculation in a larger integer type and then truncating the result.
For arithmetic operations, this problem has been addressed: Each integer type has wrapping_*
, saturating_*
and checked_*
operations. For numeric casts, options are more limited:
-
The equivalent to
checked_*
istry_into
-
The equivalent to
wrapping_*
isas
-
There is no equivalent to
saturating_*
for integer casts
Why does this require fixing, you may ask? The as
operator is used for many type conversions, both lossless and lossy. It has different behavior (truncating, wrapping, saturating, rounding) depending on the types, explained here.
The main problem is that the name of the as
operator is too vague, it doesn't describe what kind of conversion is performed. Furthermore, people often choose as
instead of .try_into().unwrap()
because it is shorter.
My proposal is to deprecate as
for lossy numeric conversions. Instead, 4 new traits are added, Truncating{From,Into}
and Saturating{From,Into}
, which work the same as From
and Into
. You can use truncating_into
when performance is very important and you know that it won't truncate, or when truncation is actually desired. saturating_into
can be used e.g. when converting from a sized to an unsized integer and negative values should become zero.
For lossless conversions, you can continue to .into()
or as
. The deprecation warning is only shown when using as
for a lossy conversion.