100 — Step By
Tutorial
The Problem
Use Rust's .step_by(n) to iterate over a range with a custom step size, producing [start, start+n, start+2n, …]. Demonstrate steps of 2, 5, 25, and 1 on integer ranges. Compare with OCaml's recursive step_by and Seq.unfold-based range_step.
🎯 Learning Outcomes
(start..end).step_by(n) for stepped ranges.step_by(n) is an iterator adapter returning StepBy<Range<T>>.step_by with .collect::<Vec<_>>() to materialise the sequence.step_by to OCaml's recursive list builder and Seq.unfoldSeq.unfold as the general anamorphism for lazy sequence generationCode Example
#![allow(clippy::all)]
// 100: Step By
#[cfg(test)]
mod tests {
#[test]
fn test_step_by() {
let v: Vec<i32> = (0..10).step_by(2).collect();
assert_eq!(v, vec![0, 2, 4, 6, 8]);
}
#[test]
fn test_step_by_25() {
let v: Vec<i32> = (0..100).step_by(25).collect();
assert_eq!(v, vec![0, 25, 50, 75]);
}
#[test]
fn test_step_by_5() {
let v: Vec<i32> = (0..20).step_by(5).collect();
assert_eq!(v, vec![0, 5, 10, 15]);
}
#[test]
fn test_step_by_1() {
let v: Vec<i32> = (0..3).step_by(1).collect();
assert_eq!(v, vec![0, 1, 2]);
}
}Key Differences
| Aspect | Rust | OCaml |
|---|---|---|
| Built-in | (start..end).step_by(n) | Manual recursive or Seq.unfold |
| Laziness | Lazy adapter | Seq.unfold is lazy; list is eager |
| Step of 1 | Identity (same as range) | Same |
| Exclusive end | start..end | Manual n >= stop |
| General unfold | std::iter::successors | Seq.unfold |
| Reverse step | Not directly (would need .rev()) | Same |
Seq.unfold is a more general primitive than .step_by: it can produce sequences where the step size changes, terminates conditionally, or derives the next state from the current in arbitrary ways. Rust's equivalent is std::iter::successors or a custom struct iterator.
OCaml Approach
OCaml's step_by start stop step uses a tail-recursive accumulator. range_step uses Seq.unfold (fun n -> if n >= stop then None else Some(n, n + step)) — the anamorphism that produces a lazy sequence from a seed and step function. Seq.unfold is more general than step_by: it can produce any sequence where the next element depends on the current state.
Full Source
#![allow(clippy::all)]
// 100: Step By
#[cfg(test)]
mod tests {
#[test]
fn test_step_by() {
let v: Vec<i32> = (0..10).step_by(2).collect();
assert_eq!(v, vec![0, 2, 4, 6, 8]);
}
#[test]
fn test_step_by_25() {
let v: Vec<i32> = (0..100).step_by(25).collect();
assert_eq!(v, vec![0, 25, 50, 75]);
}
#[test]
fn test_step_by_5() {
let v: Vec<i32> = (0..20).step_by(5).collect();
assert_eq!(v, vec![0, 5, 10, 15]);
}
#[test]
fn test_step_by_1() {
let v: Vec<i32> = (0..3).step_by(1).collect();
assert_eq!(v, vec![0, 1, 2]);
}
}#[cfg(test)]
mod tests {
#[test]
fn test_step_by() {
let v: Vec<i32> = (0..10).step_by(2).collect();
assert_eq!(v, vec![0, 2, 4, 6, 8]);
}
#[test]
fn test_step_by_25() {
let v: Vec<i32> = (0..100).step_by(25).collect();
assert_eq!(v, vec![0, 25, 50, 75]);
}
#[test]
fn test_step_by_5() {
let v: Vec<i32> = (0..20).step_by(5).collect();
assert_eq!(v, vec![0, 5, 10, 15]);
}
#[test]
fn test_step_by_1() {
let v: Vec<i32> = (0..3).step_by(1).collect();
assert_eq!(v, vec![0, 1, 2]);
}
}
Deep Comparison
Core Insight
step_by skips elements at regular intervals — useful for sampling, pagination, and grid traversal
OCaml Approach
Rust Approach
Comparison Table
| Feature | OCaml | Rust |
|---|---|---|
| See | example.ml | example.rs |
Exercises
std::iter::successors(Some((0u64, 1u64)), |&(a, b)| Some((b, a + b))).map(|(a, _)| a)..step_by to iterate over every third element of a slice via (0..v.len()).step_by(3).map(|i| v[i]).decreasing_step(start: i32, stop: i32, step: usize) -> Vec<i32> using .rev() after .step_by.Seq.unfold in OCaml to implement a sequence that doubles the step on each iteration: 0, 1, 3, 7, 15, ….step_by(1) performance vs a plain range — verify they compile to the same output with cargo bench.