#245 Jul 7, 2026

245. rem_euclid — The Modulo That Never Goes Negative

-1 % 5 is -1 in Rust, not 4% keeps the sign of the dividend. When you want a true wrap-around index or clock value, (-1i32).rem_euclid(5) gives you the 4 you actually meant.

Why % bites you

Rust’s % is a remainder, not a mathematical modulo. Its result takes the sign of the left operand:

1
2
assert_eq!(-1 % 5, -1);
assert_eq!(-8 % 3, -2);

That’s fine for the positive case, but the moment a negative number sneaks in — a decrementing index, an offset that can go below zero — you get a negative result. Feed that into arr[idx as usize] and you either panic or cast into a giant number.

rem_euclid always lands in [0, n)

1
2
3
assert_eq!((-1i32).rem_euclid(5), 4);
assert_eq!((-8i32).rem_euclid(3), 1);
assert_eq!(7i32.rem_euclid(5), 2);

For a positive divisor, rem_euclid is guaranteed to return a non-negative result strictly less than the divisor — exactly what “wrap around” should mean.

Where it pays off: wrapping an index

Stepping backwards through a ring buffer is the textbook case. With % you’d have to add the length back in by hand to avoid going negative:

1
2
3
4
5
6
7
8
9
let buf = ['a', 'b', 'c', 'd'];
let len = buf.len() as i32;

// Move one step left from index 0, wrapping to the end
let mut idx = 0i32;
idx = (idx - 1).rem_euclid(len);

assert_eq!(idx, 3);
assert_eq!(buf[idx as usize], 'd');

No + len fixup, no branch on “did it go negative.” The same trick handles clock arithmetic ((hour + delta).rem_euclid(24)) and angle normalization.

div_euclid is its partner: (-8).div_euclid(3) == -3, and the identity a == b * a.div_euclid(b) + a.rem_euclid(b) always holds. Whenever you catch yourself writing ((x % n) + n) % n to force a positive remainder, that’s rem_euclid spelled the hard way.

← Previous 244. clone_from — Reuse the Buffer You Already Have Instead of Reallocating Next → 246. Iterator::peekable — Look at the Next Item Without Consuming It