Search code examples
rustintegerinteger-overflowunsigned-integer

how to add a negative i32 number to an usize variable?


Such as:

let mut a : usize = 0xFF;
a += -1;  // -1 may be from other variable, so there can't be a -= 1;
println!("{}", a);

The output is:

error[E0277]: the trait bound `usize: std::ops::Neg` is not satisfied

anyway?


Solution

  • Your attempt does not work because in Rust, you can only do operations between strictly similar types. Rust does not do any implicit numeric cast because it would be a shoot in the foot as you can see below: you have a special case and several ways to have an overflow.

    One way to go is to cast the absolute value of the i32 to usize and add or remove it according to whether it is negative or positive. However, you must handle the special case of the minimal value that overflows when you take its absolute value:

    fn add(u: usize, i: i32) -> usize {
        if i.is_negative() {
            u - i.wrapping_abs() as u32 as usize
        } else {
            u + i as usize
        }
    }
    
    fn main() {
        let u = 7;
        let i1 = -1;
        let i2 = 1;
        let min = -2_147_483_648;
    
        assert_eq!(add(u, i1), 6);
        assert_eq!(add(u, i2), 8);
        assert_eq!(add(3_000_000_000, min), 852_516_352);
    }
    

    You may also verify the overflows:

    fn add(u: usize, i: i32) -> Option<usize> {
        if i.is_negative() {
            u.checked_sub(i.wrapping_abs() as u32 as usize)
        } else {
            u.checked_add(i as usize)
        }
    }
    
    fn main() {
        let u = 7;
        let i1 = -1;
        let i2 = 1;
    
        assert_eq!(add(u, i1), Some(6));
        assert_eq!(add(u, i2), Some(8));
        assert_eq!(add(0, -1), None);
    }