Problem overview
The task is to print all the odd numbers from 7 through 23, one per line, with a for loop. The expected output is 7, 9, 11, 13, 15, 17, 19, 21 and 23.
The previous lesson printed consecutive numbers, where the loop variable increases by 1. Here the numbers are two apart, so the range has to take bigger steps.
Approach: a range with a step
A plain Rust range always moves by 1, and it has no step setting in its syntax. To change the step, you call the method step_by on the range. It returns a new iterator that takes every n-th value of the original.
for i in (2..=10).step_by(2) {
println!("{}", i);
}
This prints 2, 4, 6, 8 and 10. The range 2..=10 holds the nine numbers from 2 to 10, and step_by(2) keeps the first and then every second one after it: 2, 4, 6, 8, 10.
Two things follow from that description:
- The sequence always starts with the range's first value. So the start of the range decides whether you get odd or even numbers. Starting from 7 and stepping by 2 gives odd numbers, so no
ifinside the loop is needed. - The sequence stops at the end of the range. If the step jumps past the end, the loop stops. Using
(2..11).step_by(2)gives the same numbers as(2..=10).step_by(2).
Step by step
- Build the range
7..=23. It starts at the first odd number to print, 7, and includes 23, the last one. - Put it in parentheses and call
.step_by(2)on it. The parentheses matter, as explained below. - Loop over the result with
for i in ..., soitakes the values 7, 9, 11, ..., 23. - Print
iwithprintln!("{}", i);. The{}is a placeholder for the value, and each call ends the line.
The solution in Rust
for i in (7..=23).step_by(2) {
println!("{}", i);
}
Details a learner might trip on:
- Why the parentheses. A method call binds tighter than the range operator. Without parentheses,
7..=23.step_by(2)tries to callstep_byon the number23, and the compiler reportscan't call method step_by on type {integer}. It even suggests surrounding the range in parentheses. - The step is a count, not an end. The argument 2 means "take every second value". It is a
usize, and it cannot be 0:step_by(0)panics withassertion failed: step != 0. - The loop variable has type
i32here, which is Rust's default for whole numbers.
The step count is easy to predict. The values are 7, 9, ..., 23, and the number of passes is (23 - 7) / 2 + 1 = 9.
An alternative you may meet
You could loop over every number from 7 through 23 and print only the odd ones with an if and the remainder operator, if i % 2 == 1. It prints the same nine numbers, but it makes 17 passes where 9 are needed, and the condition is one more thing to get wrong. With step_by, the odd numbers come straight from the start and the step.
Edge cases and common mistakes
- Starting at the wrong number.
(6..=23).step_by(2)gives 6, 8, ..., 22, which are even. The start decides the parity. - Missing the last value.
(7..23).step_by(2)stops at 21, because 23 is excluded. - Forgetting the parentheses, which is a compile error.
- Using a step of 0, which panics at run time.
- Thinking the step is the end.
step_by(2)does not limit the loop to two passes.
Testing
(7..=23).step_by(2)prints 7, 9, 11, 13, 15, 17, 19, 21, 23.(2..=10).step_by(2)prints 2, 4, 6, 8, 10.(0..10).step_by(3)prints 0, 3, 6, 9.(5..=5).step_by(2)prints 5 only.
