ExamplesBy LevelBy TopicLearning Paths
879 Intermediate

879-iterator-trait — Iterator Trait

Functional Programming

Tutorial

The Problem

Iteration is fundamental to every program. Languages handle it differently: C uses index-based loops, Java uses Iterable/Iterator interfaces, Python uses __iter__/__next__. Rust's Iterator trait requires only one method — fn next(&mut self) -> Option<Self::Item> — and provides over 70 adapter and consumer methods for free. This design unifies all iteration patterns: ranges, collections, generators, and lazy sequences all implement the same interface. OCaml's Seq module serves a similar role for lazy sequences, and List provides eager equivalents. Understanding the Iterator trait is essential for idiomatic Rust.

🎯 Learning Outcomes

  • • Implement the Iterator trait with only the next method
  • • Understand that all other iterator methods are provided automatically via default implementations
  • • Build both finite and infinite custom iterators
  • • Implement IntoIterator to make custom types work in for loops
  • • Compare Rust's Iterator with OCaml's Seq lazy sequence abstraction
  • Code Example

    struct Range { current: i32, end_: i32 }
    
    impl Iterator for Range {
        type Item = i32;
        fn next(&mut self) -> Option<i32> {
            if self.current >= self.end_ { None }
            else { let v = self.current; self.current += 1; Some(v) }
        }
    }

    Key Differences

  • Method vs function: Rust iterator methods are called on the value (iter.map(f)); OCaml uses module-level functions (Seq.map f seq).
  • Infinite sequences: Both support infinite iterators; Rust via Option::Some always, OCaml via Seq.Cons always.
  • Laziness: Rust iterators are lazy by default (adapters don't evaluate until consumed); OCaml Seq is explicitly lazy via unit ->.
  • Mutability: Rust Iterator::next takes &mut self (mutable internal state); OCaml sequences are immutable — each step returns a new sequence.
  • OCaml Approach

    OCaml's Seq module uses a lazy list: type 'a t = unit -> 'a node where node = Nil | Cons of 'a * 'a t. A custom sequence is a function returning the next node lazily. The Seq module provides map, filter, take, flat_map as standalone functions rather than methods. For eager iteration, OCaml uses List.iter, Array.iter, or explicit recursion. Seq.of_list and List.of_seq convert between representations.

    Full Source

    //! Example 879: Iterator Trait
    //!
    //! OCaml's `Seq` and hand-rolled `{ next : unit -> 'a option }` records map
    //! directly onto Rust's [`Iterator`] trait. Implementing the trait for a
    //! custom struct yields all of the standard adaptors (`map`, `filter`,
    //! `take`, `sum`, ...) for free, so the OCaml helpers `iter_map`,
    //! `iter_filter`, and `take` collapse into method calls.
    
    /// A bounded half-open integer range `[start, stop)`.
    ///
    /// Mirrors the OCaml `range` / `range_iter` functions.
    pub struct Range {
        current: i64,
        stop: i64,
    }
    
    impl Range {
        /// Creates a range yielding values from `start` (inclusive) to `stop`
        /// (exclusive).
        pub fn new(start: i64, stop: i64) -> Self {
            Self {
                current: start,
                stop,
            }
        }
    }
    
    impl Iterator for Range {
        type Item = i64;
    
        fn next(&mut self) -> Option<Self::Item> {
            if self.current >= self.stop {
                None
            } else {
                let v = self.current;
                self.current += 1;
                Some(v)
            }
        }
    }
    
    /// An unbounded counter starting at `n`, incrementing by one each call.
    ///
    /// Mirrors OCaml's `counter_from`. Pair with [`Iterator::take`] to bound it.
    pub struct Counter {
        current: i64,
    }
    
    impl Counter {
        /// Creates a counter whose first emitted value is `n`.
        pub fn from(n: i64) -> Self {
            Self { current: n }
        }
    }
    
    impl Iterator for Counter {
        type Item = i64;
    
        fn next(&mut self) -> Option<Self::Item> {
            let v = self.current;
            self.current += 1;
            Some(v)
        }
    }
    
    /// Collects any iterator into a `Vec`.
    ///
    /// Alias for [`Iterator::collect`] that matches the shape of OCaml's
    /// `seq_to_list` / `iter_to_list`.
    pub fn to_vec<I: Iterator>(it: I) -> Vec<I::Item> {
        it.collect()
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn range_yields_half_open_interval() {
            let v: Vec<i64> = Range::new(1, 6).collect();
            assert_eq!(v, vec![1, 2, 3, 4, 5]);
        }
    
        #[test]
        fn range_empty_when_start_ge_stop() {
            assert_eq!(Range::new(5, 5).count(), 0);
            assert_eq!(Range::new(6, 5).count(), 0);
        }
    
        #[test]
        fn map_and_filter_compose_like_ocaml_helpers() {
            let result: Vec<i64> = Range::new(1, 11)
                .map(|x| x * 2)
                .filter(|x| *x <= 10)
                .collect();
            assert_eq!(result, vec![2, 4, 6, 8, 10]);
        }
    
        #[test]
        fn counter_with_take_is_bounded() {
            let v: Vec<i64> = Counter::from(0).take(5).collect();
            assert_eq!(v, vec![0, 1, 2, 3, 4]);
        }
    
        #[test]
        fn counter_starts_at_given_offset() {
            let v: Vec<i64> = Counter::from(100).take(3).collect();
            assert_eq!(v, vec![100, 101, 102]);
        }
    
        #[test]
        fn to_vec_matches_collect() {
            assert_eq!(to_vec(Range::new(0, 4)), vec![0, 1, 2, 3]);
        }
    
        #[test]
        fn fold_builds_list_in_forward_order() {
            // OCaml's `seq_to_list` folds left to build a reversed list and then
            // reverses it. Rust's `fold` can push directly for forward order.
            let v = Range::new(1, 4).fold(Vec::new(), |mut acc, x| {
                acc.push(x);
                acc
            });
            assert_eq!(v, vec![1, 2, 3]);
        }
    
        #[test]
        fn sum_and_product_via_iterator_trait() {
            let sum: i64 = Range::new(1, 5).sum();
            let product: i64 = Range::new(1, 5).product();
            assert_eq!(sum, 10);
            assert_eq!(product, 24);
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn range_yields_half_open_interval() {
            let v: Vec<i64> = Range::new(1, 6).collect();
            assert_eq!(v, vec![1, 2, 3, 4, 5]);
        }
    
        #[test]
        fn range_empty_when_start_ge_stop() {
            assert_eq!(Range::new(5, 5).count(), 0);
            assert_eq!(Range::new(6, 5).count(), 0);
        }
    
        #[test]
        fn map_and_filter_compose_like_ocaml_helpers() {
            let result: Vec<i64> = Range::new(1, 11)
                .map(|x| x * 2)
                .filter(|x| *x <= 10)
                .collect();
            assert_eq!(result, vec![2, 4, 6, 8, 10]);
        }
    
        #[test]
        fn counter_with_take_is_bounded() {
            let v: Vec<i64> = Counter::from(0).take(5).collect();
            assert_eq!(v, vec![0, 1, 2, 3, 4]);
        }
    
        #[test]
        fn counter_starts_at_given_offset() {
            let v: Vec<i64> = Counter::from(100).take(3).collect();
            assert_eq!(v, vec![100, 101, 102]);
        }
    
        #[test]
        fn to_vec_matches_collect() {
            assert_eq!(to_vec(Range::new(0, 4)), vec![0, 1, 2, 3]);
        }
    
        #[test]
        fn fold_builds_list_in_forward_order() {
            // OCaml's `seq_to_list` folds left to build a reversed list and then
            // reverses it. Rust's `fold` can push directly for forward order.
            let v = Range::new(1, 4).fold(Vec::new(), |mut acc, x| {
                acc.push(x);
                acc
            });
            assert_eq!(v, vec![1, 2, 3]);
        }
    
        #[test]
        fn sum_and_product_via_iterator_trait() {
            let sum: i64 = Range::new(1, 5).sum();
            let product: i64 = Range::new(1, 5).product();
            assert_eq!(sum, 10);
            assert_eq!(product, 24);
        }
    }

    Deep Comparison

    Comparison: Iterator Trait

    Core Implementation

    OCaml — Seq:

    let range start stop =
      let rec aux n () =
        if n >= stop then Seq.Nil
        else Seq.Cons (n, aux (n + 1))
      in
      aux start
    

    Rust — Iterator trait:

    struct Range { current: i32, end_: i32 }
    
    impl Iterator for Range {
        type Item = i32;
        fn next(&mut self) -> Option<i32> {
            if self.current >= self.end_ { None }
            else { let v = self.current; self.current += 1; Some(v) }
        }
    }
    

    Map/Filter (Manual vs Free)

    OCaml — Must implement manually for custom iterators:

    let iter_map f it =
      { next = fun () -> match it.next () with
        | None -> None | Some v -> Some (f v) }
    

    Rust — Free from Iterator trait:

    // Just implementing next() gives you:
    Range::new(1, 6).map(|x| x * 2).filter(|x| x > 5).collect::<Vec<_>>()
    

    Infinite Sequences

    OCaml:

    let counter_from n =
      let c = ref n in
      { next = fun () -> let v = !c in c := v + 1; Some v }
    
    let first5 = take 5 (counter_from 0)
    

    Rust:

    impl Iterator for Counter {
        type Item = u64;
        fn next(&mut self) -> Option<u64> {
            let v = self.current; self.current += 1; Some(v)
        }
    }
    
    let first5: Vec<u64> = Counter::from(0).take(5).collect();
    

    Exercises

  • Implement a Primes iterator that yields prime numbers lazily using a sieve or trial division.
  • Implement IntoIterator for a custom Grid<T> type that yields elements in row-major order.
  • Implement a Zip2<A: Iterator, B: Iterator> struct that pairs elements from two iterators, stopping when either is exhausted.
  • Open Source Repos