#216 Jun 21, 2026

216. unsigned_abs — i32::MIN Has No Positive Twin, So .abs() Overflows

(-5i32).abs() is fine, but i32::MIN.abs() panics — the magnitude 2147483648 doesn’t fit back into an i32. unsigned_abs returns the magnitude in the unsigned type, where it always fits.

Two’s complement isn’t symmetric. An i32 reaches down to -2147483648 but only up to 2147483647, so the most-negative value has no positive counterpart. That makes abs a landmine on exactly one input:

1
2
3
4
5
let x: i32 = -5;
assert_eq!(x.abs(), 5); // fine

let edge = i32::MIN;
let m = edge.abs(); // panics in debug, wraps to i32::MIN in release

The same trap hides inside the classic (a - b).abs() distance trick, which is why abs_diff exists for differences. But when you have a single signed value and just want its magnitude, the fix is unsigned_abs:

1
2
3
let edge = i32::MIN;

assert_eq!(edge.unsigned_abs(), 2147483648u32);

It returns the unsigned sibling type (i32u32, i8u8, …), which is wide enough to hold the magnitude of every input — including the awkward one — so it can never overflow:

1
2
3
assert_eq!((-5i32).unsigned_abs(), 5u32);
assert_eq!((127i8).unsigned_abs(), 127u8);
assert_eq!(i8::MIN.unsigned_abs(), 128u8); // .abs() would panic here

If you genuinely need a signed result and want to handle the edge instead of ignoring it, checked_abs hands you an Option and saturating_abs clamps to MAX:

1
2
assert_eq!(i32::MIN.checked_abs(), None);
assert_eq!(i32::MIN.saturating_abs(), i32::MAX);

Any time you take the absolute value of a signed integer that could conceivably be MIN — parsed input, a subtraction result, a delta from untrusted data — reach for unsigned_abs instead of abs and the panic simply can’t happen.

← Previous 215. next_multiple_of — Round Up to a Multiple Without the +m-1 Dance