Rust's rem_euclid() - The Modulo You Actually Want

Having spent a bit of time working with Python recently, I hit a surprising gotcha with Rust’s % operator today.

The Problem

I was implementing a circular dial (positions 0–99) that wraps around. In Python, this just works:

position = 5
position = (position - 10) % 100 # = 95 ✅

But in Rust, the same logic fails:

let mut position = 5;
position = (position - 10) % 100; // = -5 ❌

Why?

Python’s % operator follows mathematical modulo when the divisor is positive. That means the result is always in the range [0, divisor)—ideal for wrap-around arithmetic.

Rust and Go, however, implement truncating division remainder, where the result takes the sign of the dividend:

  • 5 % 3 = 2
  • -5 % 3 = -2 ← negative remainder!

This breaks circular wrapping because -5 is not a valid position on a 0–99 dial.

The Solution: rem_euclid()

Rust provides rem_euclid() for proper Euclidean modular arithmetic:

let mut position = 5;
position = (position - 10).rem_euclid(100); // = 95 ✅

rem_euclid() guarantees a result in [0, divisor), matching the behavior people typically expect when they think “modulo.”

When to Use It

Use rem_euclid() whenever you need true modular arithmetic, especially:

  • Circular buffers / arrays — wrapping indices
  • Game coordinates — toroidal maps
  • Time calculations — hour/day/week cycles
  • Hash table indexing — always-positive indices

Use % when you specifically want the remainder, such as checking divisibility or keeping track of signed offsets.

Quick Reference

Regular remainder (can be negative):

-5 % 3 == -2

Euclidean remainder (always in [0, divisor)):

(-5_i32).rem_euclid(3) == 1

In practice:

position = (position - distance).rem_euclid(100); // Circular dial
index = (index + offset).rem_euclid(len); // Array wrapping

Rule of Thumb

If you’re thinking “modulo” for wrapping behavior, you want rem_euclid(), not %.


This bit me while solving Advent of Code 2025 Day 1, where I needed to track a dial rotating through positions 0–99.