884-infinite-iterators — Infinite Iterators
Tutorial
The Problem
Many algorithms require generating sequences without a predetermined endpoint: cycling through a palette, repeating a default value, generating an arithmetic sequence of any length. In languages without lazy evaluation, this requires explicit loop counters and early exits. Rust provides std::iter::repeat, repeat_with, cycle, and successors as built-in infinite iterator constructors. These integrate naturally with .take(n) to produce finite results. Haskell's iterate and OCaml's Seq.unfold serve the same role. This example surveys all the standard infinite iterator patterns in Rust.
🎯 Learning Outcomes
repeat, repeat_with, and cycle for constant and periodic sequencesstd::iter::successors to implement OCaml's iterate functionfrom_fn with mutable state for stateful infinite generatorsunfold using from_fn for functional-style generationCode Example
std::iter::repeat(42).take(5).collect::<Vec<_>>() // [42, 42, 42, 42, 42]Key Differences
repeat, repeat_with, cycle, successors, from_fn as distinct constructors; OCaml unifies everything under Seq.unfold.successors terminates when the closure returns None; repeat and cycle never terminate — .take(n) is required.from_fn captures mutable state in a closure; OCaml threads state as the unfold seed.repeat and cycle are zero-allocation; OCaml's Seq allocates a new closure thunk per step.OCaml Approach
OCaml's equivalent is Seq.unfold: ('a -> ('b * 'a) option) -> 'a -> 'b Seq.t. Seq.repeat x = Seq.unfold (fun () -> Some(x, ())) (). Seq.cycle is not in standard OCaml but trivially expressible via Seq.unfold with modular index state. iterate f x = Seq.unfold (fun s -> Some(s, f s)) x. The OCaml approach is more uniform (everything is unfold) while Rust provides specialized constructors for common patterns.
Full Source
#![allow(clippy::all)]
// Example 090: Infinite Iterators
// iterate, repeat, cycle, unfold
// === Approach 1: repeat and cycle ===
fn repeat_demo() -> Vec<i32> {
std::iter::repeat(42).take(5).collect()
}
fn cycle_demo() -> Vec<i32> {
[1, 2, 3].iter().copied().cycle().take(7).collect()
}
// repeat_with for computed values
fn repeat_with_demo() -> Vec<f64> {
let mut n = 1.0;
std::iter::repeat_with(move || {
let v = n;
n *= 2.0;
v
})
.take(5)
.collect()
}
// === Approach 2: successors (like OCaml iterate) ===
fn iterate<T: Clone>(init: T, f: impl Fn(&T) -> T) -> impl Iterator<Item = T> {
std::iter::successors(Some(init), move |prev| Some(f(prev)))
}
fn doubles_from(n: u64) -> impl Iterator<Item = u64> {
iterate(n, |x| x * 2)
}
fn add_step(step: i32) -> impl Iterator<Item = i32> {
iterate(0, move |x| x + step)
}
// === Approach 3: from_fn (like OCaml unfold) ===
fn unfold<T, S, F>(init: S, f: F) -> impl Iterator<Item = T>
where
F: Fn(&S) -> Option<(T, S)>,
{
let mut state = Some(init);
std::iter::from_fn(move || {
let s = state.take()?;
let (value, next) = f(&s)?;
state = Some(next);
Some(value)
})
}
fn fibonacci_unfold() -> impl Iterator<Item = u64> {
unfold((0u64, 1u64), |&(a, b)| Some((a, (b, a + b))))
}
fn digits(mut n: u64) -> Vec<u64> {
if n == 0 {
return vec![0];
}
let mut result: Vec<u64> =
unfold(n, |&n| if n == 0 { None } else { Some((n % 10, n / 10)) }).collect();
result.reverse();
result
}
// Combine infinite iterators
fn interleave<T>(
a: impl Iterator<Item = T>,
b: impl Iterator<Item = T>,
) -> impl Iterator<Item = T> {
a.zip(b)
.flat_map(|(x, y)| std::iter::once(x).chain(std::iter::once(y)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_repeat() {
assert_eq!(repeat_demo(), vec![42, 42, 42, 42, 42]);
}
#[test]
fn test_cycle() {
assert_eq!(cycle_demo(), vec![1, 2, 3, 1, 2, 3, 1]);
}
#[test]
fn test_doubles() {
let v: Vec<u64> = doubles_from(1).take(5).collect();
assert_eq!(v, vec![1, 2, 4, 8, 16]);
}
#[test]
fn test_iterate_add() {
let v: Vec<i32> = add_step(3).take(5).collect();
assert_eq!(v, vec![0, 3, 6, 9, 12]);
}
#[test]
fn test_fibonacci_unfold() {
let v: Vec<u64> = fibonacci_unfold().take(8).collect();
assert_eq!(v, vec![0, 1, 1, 2, 3, 5, 8, 13]);
}
#[test]
fn test_digits() {
assert_eq!(digits(12345), vec![1, 2, 3, 4, 5]);
assert_eq!(digits(0), vec![0]);
assert_eq!(digits(9), vec![9]);
}
#[test]
fn test_interleave() {
let v: Vec<i32> = interleave(0..5, 10..15).collect();
assert_eq!(v, vec![0, 10, 1, 11, 2, 12, 3, 13, 4, 14]);
}
#[test]
fn test_successors_collatz() {
let collatz: Vec<u64> = std::iter::successors(Some(6u64), |&n| {
if n == 1 {
None
} else if n % 2 == 0 {
Some(n / 2)
} else {
Some(3 * n + 1)
}
})
.collect();
assert_eq!(collatz, vec![6, 3, 10, 5, 16, 8, 4, 2, 1]);
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_repeat() {
assert_eq!(repeat_demo(), vec![42, 42, 42, 42, 42]);
}
#[test]
fn test_cycle() {
assert_eq!(cycle_demo(), vec![1, 2, 3, 1, 2, 3, 1]);
}
#[test]
fn test_doubles() {
let v: Vec<u64> = doubles_from(1).take(5).collect();
assert_eq!(v, vec![1, 2, 4, 8, 16]);
}
#[test]
fn test_iterate_add() {
let v: Vec<i32> = add_step(3).take(5).collect();
assert_eq!(v, vec![0, 3, 6, 9, 12]);
}
#[test]
fn test_fibonacci_unfold() {
let v: Vec<u64> = fibonacci_unfold().take(8).collect();
assert_eq!(v, vec![0, 1, 1, 2, 3, 5, 8, 13]);
}
#[test]
fn test_digits() {
assert_eq!(digits(12345), vec![1, 2, 3, 4, 5]);
assert_eq!(digits(0), vec![0]);
assert_eq!(digits(9), vec![9]);
}
#[test]
fn test_interleave() {
let v: Vec<i32> = interleave(0..5, 10..15).collect();
assert_eq!(v, vec![0, 10, 1, 11, 2, 12, 3, 13, 4, 14]);
}
#[test]
fn test_successors_collatz() {
let collatz: Vec<u64> = std::iter::successors(Some(6u64), |&n| {
if n == 1 {
None
} else if n % 2 == 0 {
Some(n / 2)
} else {
Some(3 * n + 1)
}
})
.collect();
assert_eq!(collatz, vec![6, 3, 10, 5, 16, 8, 4, 2, 1]);
}
}
Deep Comparison
Comparison: Infinite Iterators
Repeat
OCaml:
let repeat x () = Seq.Cons (x, repeat x)
seq_take 5 (repeat 42) (* [42; 42; 42; 42; 42] *)
Rust:
std::iter::repeat(42).take(5).collect::<Vec<_>>() // [42, 42, 42, 42, 42]
Cycle
OCaml:
let rec cycle lst () =
let rec from = function
| [] -> cycle lst ()
| x :: rest -> Seq.Cons (x, fun () -> from rest)
in from lst
Rust:
[1, 2, 3].iter().copied().cycle().take(7).collect::<Vec<_>>()
Iterate (Successors)
OCaml:
let iterate f x =
let rec aux v () = Seq.Cons (v, aux (f v)) in
aux x
let doubles = iterate (fun x -> x * 2) 1
Rust:
std::iter::successors(Some(1u64), |&x| Some(x * 2))
Unfold
OCaml:
let unfold f init =
let rec aux state () =
match f state with
| None -> Seq.Nil
| Some (value, next) -> Seq.Cons (value, aux next)
in aux init
let fib = unfold (fun (a,b) -> Some (a, (b, a+b))) (0, 1)
Rust:
fn fibonacci() -> impl Iterator<Item = u64> {
let mut state = (0u64, 1u64);
std::iter::from_fn(move || {
let val = state.0;
state = (state.1, state.0 + state.1);
Some(val)
})
}
Exercises
powers_of_two as an infinite iterator and use it to find the first power of 2 exceeding one million.cycle to implement round_robin<T: Clone>(vecs: &[Vec<T>]) -> impl Iterator<Item = T> that cycles through elements in rotation.collatz_lengths() as an infinite iterator yielding the Collatz sequence length for n = 1, 2, 3, ...