ExamplesBy LevelBy TopicLearning Paths
100 Intermediate

100 — Step By

Functional Programming

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

  • • Use (start..end).step_by(n) for stepped ranges
  • • Understand that .step_by(n) is an iterator adapter returning StepBy<Range<T>>
  • • Combine .step_by with .collect::<Vec<_>>() to materialise the sequence
  • • Map Rust's .step_by to OCaml's recursive list builder and Seq.unfold
  • • Recognise Seq.unfold as the general anamorphism for lazy sequence generation
  • • Understand the difference between range steps and array strides
  • Code 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

    AspectRustOCaml
    Built-in(start..end).step_by(n)Manual recursive or Seq.unfold
    LazinessLazy adapterSeq.unfold is lazy; list is eager
    Step of 1Identity (same as range)Same
    Exclusive endstart..endManual n >= stop
    General unfoldstd::iter::successorsSeq.unfold
    Reverse stepNot 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]);
        }
    }
    ✓ Tests Rust test suite
    #[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

  • • See example.ml for implementation
  • Rust Approach

  • • See example.rs for implementation
  • Comparison Table

    FeatureOCamlRust
    Seeexample.mlexample.rs

    Exercises

  • Generate Fibonacci numbers using std::iter::successors(Some((0u64, 1u64)), |&(a, b)| Some((b, a + b))).map(|(a, _)| a).
  • Use .step_by to iterate over every third element of a slice via (0..v.len()).step_by(3).map(|i| v[i]).
  • Implement decreasing_step(start: i32, stop: i32, step: usize) -> Vec<i32> using .rev() after .step_by.
  • Use Seq.unfold in OCaml to implement a sequence that doubles the step on each iteration: 0, 1, 3, 7, 15, …
  • Compare .step_by(1) performance vs a plain range — verify they compile to the same output with cargo bench.
  • Open Source Repos