#219 Jun 24, 2026

219. checked_add_signed — Move an Unsigned Index by a Signed Delta, No Cast

You have a usize index and a delta: isize that might be negative. idx + delta won’t even compile, and casting your way around it wraps silently on underflow.

The naive fixes are both wrong in their own way:

1
2
3
4
5
let idx: usize = 3;
let delta: isize = -2;

// let next = idx + delta;          // error: cannot add `isize` to `usize`
let next = (idx as isize + delta) as usize; // compiles, wraps on underflow

That cast dance hides bugs: subtract past zero and you get a gigantic index instead of an error.

checked_add_signed adds a signed offset to an unsigned integer and hands back an OptionNone exactly when the result would underflow below zero or overflow the type:

1
2
3
4
5
let idx: usize = 3;

assert_eq!(idx.checked_add_signed(2),  Some(5));
assert_eq!(idx.checked_add_signed(-2), Some(1));
assert_eq!(idx.checked_add_signed(-4), None);   // would go below 0

So moving a cursor inside bounds becomes one honest expression — no as, no manual if delta < 0 branch:

1
2
3
4
5
6
7
fn move_cursor(pos: usize, delta: isize, len: usize) -> Option<usize> {
    pos.checked_add_signed(delta).filter(|&p| p < len)
}

assert_eq!(move_cursor(2, 1, 5), Some(3));
assert_eq!(move_cursor(0, -1, 5), None); // off the front
assert_eq!(move_cursor(4, 1, 5), None);  // off the back

It’s available on every unsigned type with its matching signed offset (u32 takes i32, usize takes isize, and so on). If you’d rather clamp than reject, the saturating_add_signed sibling pins the result to the type’s bounds instead of returning None. And as of Rust 1.90 the _sub_signed variants round out the set for subtracting a signed amount.

← Previous 218. str::match_indices — Find Every Match and Its Position in One Pass Next → 220. ilog10 — Count an Integer's Digits Without Formatting It to a String