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:
a.rem_euclid(b)
Example:
fn main() {
println!("{}", (-21).rem_euclid(4)); // 3
}
This returns the Euclidean remainder, which matches the mathematical modulo behavior most programmers want for positive divisors.
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.
Example: wrapping an index
fn main() {
let len = 5;
let index = -1;
let wrapped = index.rem_euclid(len);
println!("{}", wrapped); // 4
}
This is useful when moving backward through a circular list.
Example: angle normalization
fn main() {
let angle = -90;
let normalized = angle.rem_euclid(360);
println!("{}", normalized); // 270
}
This converts a negative angle into the standard 0..360 range.
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
resultbecomes3.println!prints3.
Now compare with %:
fn main() {
let value = -21;
let result = value % 4;
println!("{}", result);
}
Step by step:
valueis-21.- Rust performs truncating division toward zero.
-21 / 4becomes-5in integer division.- Rust computes:
-21 - (-5 * 4) = -1
- So the remainder is
-1. println!prints-1.
That is why % and .rem_euclid() differ for negative numbers.
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 0 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.rem_euclid(len)
}
This makes intent obvious at the call site.
Avoiding custom arithmetic
Instead of writing:
((a % b) + b) % b
developers usually prefer:
a.rem_euclid(b)
because it communicates meaning directly and avoids subtle mistakes.
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:
(-21).rem_euclid(4)
Mistake 4: Ignoring division by zero
Broken code:
let x = 10.rem_euclid(0);
This will panic at runtime.
Avoid it by validating the divisor:
fn safe_mod(a: i32, b: i32) -> Option<i32> {
if b == 0 {
None
} else {
Some(a.rem_euclid(b))
}
}
Mistake 5: Assuming all languages treat % the same way
Different languages define integer division and remainder differently. If you switch languages, always check how negative operands behave.
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 .rem_euclid()
- Use
%when you want Rust's standard remainder behavior. - Use
.rem_euclid()when you want a result normalized by the divisor.
Built-in method vs manual formula
| Approach | Pros | Cons |
|---|---|---|
.rem_euclid() | Clear, standard, expressive | Requires knowing the method exists |
((a % b) + b) % b | Familiar mathematical workaround | Less readable, repeated logic |
Positive vs negative inputs
For positive values, % and .rem_euclid() often match:
21 % 4 == 21.rem_euclid(4) // both are 1
The difference becomes important when the left operand is negative.
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
(-21).rem_euclid(4) // 3
When to use what
- Use
%for standard remainder math. - Use
.rem_euclid()for:- wrapping indexes
- clocks and calendars
- normalized angles
- cyclic state
Safe helper
fn safe_mod(a: i32, b: i32) -> Option<i32> {
if b == 0 {
None
} else {
Some(a.rem_euclid(b))
}
}
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.
What happens if I use zero as the divisor?
It will panic at runtime for integer types. Always validate the divisor if zero is possible.
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
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.