879-iterator-trait — Iterator Trait
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
Iterator trait with only the next methodIntoIterator to make custom types work in for loopsIterator with OCaml's Seq lazy sequence abstractionCode 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
iter.map(f)); OCaml uses module-level functions (Seq.map f seq).Option::Some always, OCaml via Seq.Cons always.Seq is explicitly lazy via unit ->.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);
}
}#[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
Primes iterator that yields prime numbers lazily using a sieve or trial division.IntoIterator for a custom Grid<T> type that yields elements in row-major order.Zip2<A: Iterator, B: Iterator> struct that pairs elements from two iterators, stopping when either is exhausted.