Question
How can I iterate over a range in Rust using a step value other than 1?
I am coming from a C++ background, where I might write a loop like this:
for (auto i = 0; i <= n; i += 2) {
// ...
}
In Rust, I typically use ranges such as 0..=n, but I do not see a third argument for specifying a custom step size. What is the idiomatic way to do this in Rust?
Short Answer
By the end of this page, you will understand how Rust ranges work, why they do not directly include a step argument, and how to use iterator methods such as step_by() to loop with custom increments. You will also learn when inclusive and exclusive ranges matter, how to trace execution, and what common mistakes to avoid.
Concept
Rust treats ranges as iterators or values that can become iterators, and iteration behavior is built up by chaining iterator methods.
Unlike C++'s classic for loop syntax, Rust does not usually put initialization, condition, and increment in one line. Instead, Rust encourages you to describe:
- what sequence you want
- how that sequence should be transformed
- what to do with each item
For a custom step size, the key idea is that you start with a range and then apply .step_by(step).
For example:
for i in (0..=n).step_by(2) {
println!("{}", i);
}
This means:
- start at
0 - go up to and including
n - take every 2nd value
This matters in real programming because many tasks do not move one item at a time. You may need to:
- process every other index
- sample data at intervals
- skip unused values
- generate sequences like
0, 5, 10, 15
Rust's iterator-based approach is powerful because the same style works for many kinds of sequences, not just numeric loops.
Mental Model
Think of a Rust range like a numbered path:
0..=10means all stepping stones from0through10.step_by(2)means you choose to land on every second stone
So instead of saying, "increase i by 2 after each loop," Rust says, "build me a sequence that already contains only the values I want."
That is the mindset shift from C++-style looping to Rust-style iteration:
- C++ often focuses on manually controlling the counter
- Rust often focuses on describing the values to iterate over
Syntax and Examples
Basic syntax
for i in (start..end).step_by(step) {
// use i
}
For an inclusive end:
for i in (start..=end).step_by(step) {
// use i
}
Example: even numbers up to n
fn main() {
let n = 10;
for i in (0..=n).step_by(2) {
println!("{}", i);
}
}
Output:
0
2
4
6
8
10
Explanation
0..=ncreates an inclusive range from0to
Step by Step Execution
Consider this code:
fn main() {
let n = 8;
for i in (0..=n).step_by(2) {
println!("value = {}", i);
}
}
Here is what happens step by step:
-
let n = 8;- A variable
nis created with value8.
- A variable
-
(0..=n)- Rust creates an inclusive range:
0, 1, 2, 3, 4, 5, 6, 7, 8
- Rust creates an inclusive range:
-
.step_by(2)- Rust transforms that sequence to take every 2nd value starting from the first one.
- The resulting values are:
0, 2, 4, 6, 8
-
for i in ...- The loop runs once for each value in that stepped sequence.
Real World Use Cases
Custom-step iteration appears often in practical Rust code.
Processing every nth item
You may want to sample data instead of reading every element.
let data = [10, 20, 30, 40, 50, 60];
for i in (0..data.len()).step_by(2) {
println!("{}", data[i]);
}
Working with grid coordinates
In graphics, game development, or simulations, you might move through positions in fixed intervals.
for x in (0..=100).step_by(10) {
println!("x = {}", x);
}
Reading structured data
If bytes are grouped in chunks, a stepped index can help.
let bytes = [, , , , , , , ];
(..bytes.()).() {
(, i);
}
Real Codebase Usage
In real projects, developers often combine step_by() with other iterator patterns.
Looping over indices carefully
When working with arrays or slices, stepped loops are often used with lengths:
let items = ["a", "b", "c", "d", "e"];
for i in (0..items.len()).step_by(2) {
println!("{} -> {}", i, items[i]);
}
This is common when you specifically need the index.
Using guard clauses before looping
If the step depends on input, validate it first.
fn print_with_step(n: usize, step: usize) {
if step == 0 {
return;
}
for i in (0..=n).step_by(step) {
println!("{}", i);
}
}
Common Mistakes
1. Forgetting parentheses around the range
Broken:
for i in 0..=10.step_by(2) {
println!("{}", i);
}
Why it is wrong:
- Method call precedence does not mean what you want here.
- Rust may try to call
step_byon10instead of on the range.
Correct:
for i in (0..=10).step_by(2) {
println!("{}", i);
}
2. Confusing .. with ..=
Broken expectation:
for i in (0..10).step_by() {
(, i);
}
Comparisons
Custom-step range vs manual counter loop
| Approach | Style | Best for | Example |
|---|---|---|---|
Range + step_by() | Idiomatic Rust | Clear numeric iteration | (0..=n).step_by(2) |
while loop with manual increment | More manual | Complex loop control | i += 2 inside loop |
for with step_by() vs while
| Feature | for + step_by() |
|---|
Cheat Sheet
Quick syntax
for i in (start..end).step_by(step) {
// end is excluded
}
for i in (start..=end).step_by(step) {
// end is included
}
Common examples
for i in (0..10).step_by(2) {}
for i in (0..=10).step_by(2) {}
for x in values.iter().step_by(2) {}
Rules to remember
- Use
.step_by(n)to skip byn - Add parentheses around the range:
(0..10).step_by(2) ..is exclusive at the end
FAQ
How do I increment by 2 in a Rust for loop?
Use .step_by(2) on a range:
for i in (0..=10).step_by(2) {
println!("{}", i);
}
How do I include the end value in a Rust range?
Use ..= instead of ...
0..=10
This includes 10.
Can I use a negative step in Rust ranges?
Not directly with a normal forward range. For reverse iteration, use .rev() on a range and then step through it.
Why does Rust use step_by() instead of a third range argument?
Rust's iteration model is based on iterators and iterator methods. This keeps ranges simple and lets you build custom behavior by chaining methods.
Should I use indices or iterate over values directly?
If you only need the elements, iterate over values directly:
Mini Project
Description
Build a small Rust program that prints numbered checkpoints for a training session. The checkpoints should be generated at a fixed interval, such as every 5 minutes. This demonstrates how to use a range with step_by() in a realistic scheduling-style task.
Goal
Create a Rust program that prints all checkpoint times from 0 up to a limit using a custom step size.
Requirements
- Read or define a total session length as an integer
- Read or define a checkpoint interval greater than 0
- Print each checkpoint from 0 up to the session length
- Include the final checkpoint only if it falls exactly on the interval
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.
Convert an Integer to a String in Rust
Learn the current Rust way to convert integers to strings, why `to_str()` no longer works, and when to use `to_string()` or `format!`.