Question
In Rust, the % operator performs remainder, not mathematical modulus. These two operations produce different results when negative numbers are involved.
For example:
-21 modulus 4should be3-21 remainder 4is-1
fn main() {
println!("{}", -21 % 4); // -1
}
If I want the mathematical modulus result in Rust, is there a built-in function or operation for it?
I found this workaround:
((a % b) + b) % b
but I would prefer to use an existing standard solution if one is available.
Short Answer
By the end of this page, you will understand the difference between remainder and modulus in Rust, why % gives negative results for some inputs, and how to get a true mathematical modulo result using the standard library. You will also see practical examples, common mistakes, and when this difference matters in real programs.
Concept
In Rust, % is the remainder operator. It is based on division that truncates toward zero.
That means:
a % b
returns the leftover part after integer division, and the result keeps the sign of a (the left operand).
For example:
fn main() {
println!("{}", -21 % 4); // -1
}
Mathematical modulus is slightly different. When people say they want a value "wrapped into the range 0..b" for a positive b, they usually mean modulo.
So for -21 mod 4, the expected result is 3, because 3 is the equivalent value in the cycle of length 4.
This matters in real programming whenever you:
- wrap array-like positions
- work with clocks or calendars
- normalize angles
- cycle through states
- map negative movement into positive ranges
Rust provides a built-in method for this exact need:
Mental Model
Think of % and modulus as two different ways of tracking position on a circular track.
- Remainder says: "How much is left over after division, keeping the direction of travel?"
- Modulus says: "Where do I land on the loop if I wrap around properly?"
Imagine a clock with 4 positions: 0, 1, 2, 3.
If you move backward 21 steps from 0, you do not want -1 as a clock position. You want the wrapped position, which is 3.
That wrapped result is what modulo gives you.
So:
- remainder is about the arithmetic leftover
- modulus is about the normalized position in a cycle
Syntax and Examples
Rust's % operator gives the remainder:
fn main() {
println!("{}", 21 % 4); // 1
println!("{}", -21 % 4); // -1
}
If you want modulo-style behavior, use .rem_euclid():
fn main() {
println!("{}", 21.rem_euclid(4)); // 1
println!("{}", (-21).rem_euclid(4)); // 3
}
Why the parentheses?
When calling a method on a negative numeric literal in Rust, you usually need parentheses:
(-21).rem_euclid(4)
Without parentheses, Rust may parse the expression differently.
Step by Step Execution
Consider this example:
fn main() {
let value = -21;
let result = value.rem_euclid(4);
println!("{}", result);
}
Step by step:
valueis set to-21.value.rem_euclid(4)asks Rust for the Euclidean remainder when dividing-21by4.- Rust finds a result
rsuch that:-21 = 4 * q + rris in the valid Euclidean range for divisor4
- The valid result is
3, because:
-21 = 4 * (-6) + 3
resultbecomes .
Real World Use Cases
Modulo-style behavior is common in many practical tasks.
Circular indexing
When moving through a menu, playlist, or game board, positions often wrap around.
let current = 0;
let previous = (current - 1).rem_euclid(5); // 4
Time calculations
Clocks wrap every 24 hours.
let hour = (-3).rem_euclid(24); // 21
Angle normalization
Graphics, robotics, and simulations often normalize angles.
let angle = (-450).rem_euclid(360); // 270
Hash buckets or partitions
When distributing values into fixed ranges, you may need results that always stay in a non-negative bucket range.
Game movement and grids
In tile maps or toroidal worlds, moving left from column should wrap to the last column.
Real Codebase Usage
In real Rust codebases, developers usually prefer .rem_euclid() over manual formulas because it is clearer and less error-prone.
Common patterns
Normalizing values into a range
let normalized = value.rem_euclid(range_size);
This is common for:
- cursor positions
- pagination offsets
- cyclic state machines
- day-of-week calculations
Guarding assumptions
If the divisor must be positive, developers often validate it first:
fn wrap(value: i32, size: i32) -> i32 {
assert!(size > 0, "size must be positive");
value.rem_euclid(size)
}
Reusable helper functions
In larger codebases, teams often hide the logic behind a named function:
fn wrap_index(index: i32, len: i32) -> i32 {
index.(len)
}
Common Mistakes
Mistake 1: Assuming % is mathematical modulo
Broken expectation:
fn main() {
println!("{}", -1 % 5); // prints -1, not 4
}
How to avoid it:
- Use
%only when you truly want remainder behavior. - Use
.rem_euclid()when you want wrapping into a non-negative range for a positive divisor.
Mistake 2: Rewriting the modulo formula manually everywhere
This works in many cases:
((a % b) + b) % b
But it is less readable and easier to misuse.
Better:
a.rem_euclid(b)
Mistake 3: Forgetting that negative literals need parentheses for method calls
Broken code:
-21.rem_euclid(4)
Correct code:
Comparisons
| Concept | Rust syntax | Result for -21 and 4 | Best use |
|---|---|---|---|
| Remainder | -21 % 4 | -1 | Arithmetic leftover with Rust's division rules |
| Euclidean remainder / modulo-style result | (-21).rem_euclid(4) | 3 | Wrapping values into a standard range |
| Manual modulo workaround | ((-21 % 4) + 4) % 4 | 3 | Works, but less clear than .rem_euclid() |
% vs
Cheat Sheet
// Remainder
let r = a % b;
// Modulo-style / Euclidean remainder
let m = a.rem_euclid(b);
Quick rules
%is remainder, not mathematical modulo..rem_euclid()gives the modulo-style result.- For positive
b,a.rem_euclid(b)is usually in the range0..b. - Calling with
b == 0will panic. - Negative numeric literals need parentheses for method calls:
(-21).rem_euclid(4)
Common examples
21 % 4 // 1
-21 % 4 // -1
21.rem_euclid(4) // 1
(-).()
FAQ
Is there a built-in modulus function in Rust?
Yes. Use .rem_euclid() for modulo-style behavior.
Why does % return a negative number in Rust?
Because % is the remainder operator, and Rust's integer division truncates toward zero.
What is the difference between remainder and modulus?
Remainder follows the division rule used by the language. Modulus usually means a wrapped result in a standard range, especially for positive divisors.
How do I get a positive result for negative numbers in Rust?
Use:
value.rem_euclid(divisor)
For example:
(-21).rem_euclid(4) // 3
Is ((a % b) + b) % b valid in Rust?
Yes, it can work as a manual workaround, but .rem_euclid() is clearer and should usually be preferred.
Does .rem_euclid() work for floating-point numbers?
Rust also provides rem_euclid for floating-point types, but be careful with floating-point precision issues.
Mini Project
Description
Build a small Rust program that wraps positions in a circular list. This demonstrates why .rem_euclid() is useful when positions can move forward or backward, including into negative values.
Goal
Create a program that takes a starting position and movement amount, then returns the wrapped position inside a fixed-size cycle.
Requirements
- Define a fixed cycle size such as 7.
- Start from a position and apply both positive and negative movement.
- Use
.rem_euclid()to keep the final position inside the valid range. - Print the original position, movement, and wrapped result.
- Show at least one example where
%would give the wrong kind of result for wrapping.
Keep learning
Related questions
Accessing Cargo Package Metadata in Rust
Learn how to read Cargo package metadata like version, name, and authors in Rust using compile-time environment macros.
Associated Types vs Generic Type Parameters in Rust: When to Use Each
Learn when to use associated types vs generic parameters in Rust traits, with clear rules, examples, and practical API design advice.
Can a Struct Extend Another Struct in Rust? Composition vs Inheritance
Learn how Rust handles struct reuse without inheritance, using composition, traits, and wrapper structs with practical examples.