rust

Why doesn't `i32 as usize` panic on overflow?


If I try to substract into the negative from an usize integer rust panics.

fn main() {
    let a: usize = 1;
    println!("{}", a - 5);    
}
error: this arithmetic operation will overflow

But I can cast an i32 to usize and get an overflow without any warnings or errors:

fn main() {
    let a: i32 = -1;
    let b: usize = a as usize;
    println!("{}", b);
}
18446744073709551615

Is there a better way to casting this? I'll just avoid casting i32 to usize or check manually for overflows, but it is surprising to me that I can get this overflow so easily in Rust.


Solution

  • as between integer types doesn't do any checks, it just reinterprets the same bytes as different types, truncating or zero/sign extending if needed (meaning overflow when the value is too large or too small). See the Reference for the exact details.

    This (as being unsafe) is one of the reasons that as is recommended against, and is slowly replaced by other, safer things (the other reason being as doing too many different things).

    The alternative for as as integer cast is to use From/Into and TryFrom/TryInto. From and Into are implemented between integers when the cast is lossless (an integer, signed or unsigned, to a bigger integer type). TryFrom/TryInto are used when there may be a loss in precision (conversions to smaller type or same size with different unsignedness) or when the size is unknown (any integer except u8 and u16 to usize and likewise, any integer except i8 and i16 to isize). They will return an error if the value doesn't fit.

    So replace your conversion with isize::try_from(a).unwrap() (or gracefully handle the error).