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
#![allow(clippy::all)]
// Example 085: Iterator Trait
// OCaml Seq → Rust Iterator
// === Approach 1: Implementing Iterator for a custom type ===
struct Range {
current: i32,
end_: i32,
}
impl Range {
fn new(start: i32, end_: i32) -> Self {
Range {
current: start,
end_,
}
}
}
impl Iterator for Range {
type Item = i32;
fn next(&mut self) -> Option<Self::Item> {
if self.current >= self.end_ {
None
} else {
let val = self.current;
self.current += 1;
Some(val)
}
}
}
// === Approach 2: Iterator with map/filter (free from trait) ===
struct Counter {
current: u64,
}
impl Counter {
fn from(start: u64) -> Self {
Counter { current: start }
}
}
impl Iterator for Counter {
type Item = u64;
fn next(&mut self) -> Option<Self::Item> {
let val = self.current;
self.current += 1;
Some(val) // infinite!
}
}
// === Approach 3: Iterator via IntoIterator ===
struct Repeat<T: Clone> {
value: T,
remaining: usize,
}
impl<T: Clone> Repeat<T> {
fn new(value: T, count: usize) -> Self {
Repeat {
value,
remaining: count,
}
}
}
impl<T: Clone> Iterator for Repeat<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
None
} else {
self.remaining -= 1;
Some(self.value.clone())
}
}
}
// Generic function working with any iterator
fn sum_first_n<I: Iterator<Item = i32>>(iter: I, n: usize) -> i32 {
iter.take(n).sum()
}
fn collect_mapped<I, F, B>(iter: I, f: F) -> Vec<B>
where
I: Iterator,
F: Fn(I::Item) -> B,
{
iter.map(f).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_range() {
let v: Vec<i32> = Range::new(1, 6).collect();
assert_eq!(v, vec![1, 2, 3, 4, 5]);
}
#[test]
fn test_empty_range() {
let v: Vec<i32> = Range::new(5, 5).collect();
assert!(v.is_empty());
}
#[test]
fn test_range_map() {
let v: Vec<i32> = Range::new(1, 4).map(|x| x * 10).collect();
assert_eq!(v, vec![10, 20, 30]);
}
#[test]
fn test_range_filter() {
let v: Vec<i32> = Range::new(1, 11).filter(|x| x % 3 == 0).collect();
assert_eq!(v, vec![3, 6, 9]);
}
#[test]
fn test_counter_take() {
let v: Vec<u64> = Counter::from(10).take(3).collect();
assert_eq!(v, vec![10, 11, 12]);
}
#[test]
fn test_counter_chain() {
let sum: u64 = Counter::from(1).take(100).sum();
assert_eq!(sum, 5050);
}
#[test]
fn test_repeat() {
let v: Vec<i32> = Repeat::new(42, 4).collect();
assert_eq!(v, vec![42, 42, 42, 42]);
}
#[test]
fn test_sum_first_n() {
let sum = sum_first_n(Range::new(1, 100), 10);
assert_eq!(sum, 55);
}
#[test]
fn test_collect_mapped() {
let v = collect_mapped(Range::new(1, 4), |x| x.to_string());
assert_eq!(v, vec!["1", "2", "3"]);
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_range() {
let v: Vec<i32> = Range::new(1, 6).collect();
assert_eq!(v, vec![1, 2, 3, 4, 5]);
}
#[test]
fn test_empty_range() {
let v: Vec<i32> = Range::new(5, 5).collect();
assert!(v.is_empty());
}
#[test]
fn test_range_map() {
let v: Vec<i32> = Range::new(1, 4).map(|x| x * 10).collect();
assert_eq!(v, vec![10, 20, 30]);
}
#[test]
fn test_range_filter() {
let v: Vec<i32> = Range::new(1, 11).filter(|x| x % 3 == 0).collect();
assert_eq!(v, vec![3, 6, 9]);
}
#[test]
fn test_counter_take() {
let v: Vec<u64> = Counter::from(10).take(3).collect();
assert_eq!(v, vec![10, 11, 12]);
}
#[test]
fn test_counter_chain() {
let sum: u64 = Counter::from(1).take(100).sum();
assert_eq!(sum, 5050);
}
#[test]
fn test_repeat() {
let v: Vec<i32> = Repeat::new(42, 4).collect();
assert_eq!(v, vec![42, 42, 42, 42]);
}
#[test]
fn test_sum_first_n() {
let sum = sum_first_n(Range::new(1, 100), 10);
assert_eq!(sum, 55);
}
#[test]
fn test_collect_mapped() {
let v = collect_mapped(Range::new(1, 4), |x| x.to_string());
assert_eq!(v, vec!["1", "2", "3"]);
}
}
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.