BBuzzard

You do not always need a struct to make an iterator. std::iter has small building blocks:

  • from_fn(closure): calls the closure each time; it returns Some to yield, None to stop.
  • successors(seed, f): each item is computed from the previous one.
  • once(v) yields one value; once_with(f) computes it lazily.
  • repeat(v) clones v forever; repeat_n(v, n) stops after n; repeat_with(f) calls f.
  • empty() yields nothing.

The infinite ones need a take or take_while downstream. Combined with chain and zip they cover most cases where you would otherwise write a one-off Iterator impl.

from_fn with captured state

    empty, from_fn, once, once_with, repeat, repeat_n, repeat_with, successors,
};

fn demo_from_fn() {
    println!("-- from_fn --");

    let mut n = 0;
    let v: Vec<_> = from_fn(|| {
        n += 1;
        if n <= 3 { Some(n) } else { None }
    })
    .collect();
    println!("{v:?}");

    // Read "records" from a buffer until it runs out.
    let mut data = "id=1;id=2;id=3".split(';');
    let ids: Vec<u32> =
        from_fn(|| data.next()?.strip_prefix("id=")?.parse().ok()).collect();
    println!("ids {ids:?}");
}

Successors

fn demo_successors() {
    println!("-- successors --");

    let powers: Vec<_> = successors(Some(1i32), |n| n.checked_mul(2)).take(6).collect();
    println!("powers {powers:?}");

    let all_u8_powers: Vec<u8> = successors(Some(1u8), |n| n.checked_mul(2)).collect();
    println!("u8 powers until overflow {all_u8_powers:?}");

    let collatz: Vec<u64> = successors(Some(6u64), |&n| {
        (n != 1).then(|| if n % 2 == 0 { n / 2 } else { 3 * n + 1 })
    })
    .collect();
    println!("collatz {collatz:?}");

    let digits: Vec<u32> = successors(Some(9045u32), |n| (*n >= 10).then_some(n / 10))
        .map(|n| n % 10)
        .collect();

    println!("digits reversed {digits:?}");
}

Once, once_with, and empty

fn demo_once_empty() {
    println!("-- once / empty --");

    let v: Vec<_> = once(1).chain(once(2)).chain(empty()).collect();
    println!("{v:?}");

    // Add a header row to a list without allocating a new Vec first.
    let rows = ["a", "b"];
    let with_header: Vec<_> = once("NAME").chain(rows).collect();
    println!("{with_header:?}");

    let lazy = once_with(|| {
        println!("  computing the one value");
        42
    });

    println!("before pulling");
    println!("got {:?}", lazy.collect::<Vec<_>>());

    let none: Vec<i32> = empty().collect();
    println!("empty {none:?}");
}

Repeat, repeat_n, repeat_with

fn demo_repeat() {
    println!("-- repeat --");

    println!("{:?}", repeat("x").take(3).collect::<Vec<_>>());
    println!("{:?}", repeat_n(0u8, 4).collect::<Vec<_>>());

    let line: String = repeat('=').take(10).collect();
    println!("{line}");

    let mut seed = 7u32;
    let pseudo: Vec<u32> = repeat_with(|| {
        seed = seed.wrapping_mul(1103515245).wrapping_add(12345);
        (seed >> 16) % 100
    })
    .take(5)
    .collect();
    println!("pseudo random {pseudo:?}");
}

Returning sources from functions

fn evens() -> impl Iterator<Item = u32> {
    (0..).step_by(2)
}

Windows over generated values

fn countdown(from: u32) -> impl Iterator<Item = u32> {
    successors(Some(from), |&n| n.checked_sub(1))
}

fn padded<'a>(items: &'a [&'a str], width: usize) -> impl Iterator<Item = &'a str> + 'a {
    items.iter().copied().chain(repeat("-")).take(width)
}

fn demo_returned_sources() {
    println!("-- returned sources --");

    println!("{:?}", evens().take(5).collect::<Vec<_>>());
    println!("{:?}", countdown(3).collect::<Vec<_>>());
    println!("{:?}", padded(&["a", "b"], 5).collect::<Vec<_>>());
}

fn demo_pairs() {
    println!("-- pairs from sources --");

    let squares = (1..).map(|n: u32| n * n);
    let gaps: Vec<_> =
        squares.clone().zip(squares.skip(1)).map(|(a, b)| b - a).take(5).collect();
    println!("gaps between squares {gaps:?}");

    let alternating: Vec<i32> =
        [1, -1].iter().copied().cycle().zip(1..=6).map(|(s, n)| s * n).collect();
    println!("alternating {alternating:?}");
}
$ cargo run --bin 32_iterator_sources
-- from_fn --
[1, 2, 3]
ids [1, 2, 3]
-- successors --
powers [1, 2, 4, 8, 16, 32]
u8 powers until overflow [1, 2, 4, 8, 16, 32, 64, 128]
collatz [6, 3, 10, 5, 16, 8, 4, 2, 1]
digits reversed [5, 4, 0, 9]
-- once / empty --
[1, 2]
["NAME", "a", "b"]
before pulling
  computing the one value
got [42]
empty []
-- repeat --
["x", "x", "x"]
[0, 0, 0, 0]
==========
pseudo random [32, 6, 68, 74, 31]
-- returned sources --
[0, 2, 4, 6, 8]
[3, 2, 1, 0]
["a", "b", "-", "-", "-"]
-- pairs from sources --
gaps between squares [3, 5, 7, 9, 11]
alternating [1, -2, 3, -4, 5, -6]

Full program

32_iterator_sources/main.rs · 149 lines
View on GitHub
// 32. Iterator sources: from_fn, successors, once, repeat, empty
//
// You do not always need a struct to make an iterator. std::iter has small building
// blocks:
//
// - from_fn(closure): calls the closure each time; it returns Some to yield, None to stop.
// - successors(seed, f): each item is computed from the previous one.
// - once(v) yields one value; once_with(f) computes it lazily.
// - repeat(v) clones v forever; repeat_n(v, n) stops after n; repeat_with(f) calls f.
// - empty() yields nothing.
//
// The infinite ones need a take or take_while downstream. Combined with chain and zip
// they cover most cases where you would otherwise write a one-off Iterator impl.
//
// Run: cargo run --bin 32_iterator_sources

use std::iter::{
    empty, from_fn, once, once_with, repeat, repeat_n, repeat_with, successors,
};

// Example 1: from_fn with captured state.
fn demo_from_fn() {
    println!("-- from_fn --");

    let mut n = 0;
    let v: Vec<_> = from_fn(|| {
        n += 1;
        if n <= 3 { Some(n) } else { None }
    })
    .collect();
    println!("{v:?}");

    // Read "records" from a buffer until it runs out.
    let mut data = "id=1;id=2;id=3".split(';');
    let ids: Vec<u32> =
        from_fn(|| data.next()?.strip_prefix("id=")?.parse().ok()).collect();
    println!("ids {ids:?}");
}

// Example 2: successors.
fn demo_successors() {
    println!("-- successors --");

    let powers: Vec<_> = successors(Some(1i32), |n| n.checked_mul(2)).take(6).collect();
    println!("powers {powers:?}");

    let all_u8_powers: Vec<u8> = successors(Some(1u8), |n| n.checked_mul(2)).collect();
    println!("u8 powers until overflow {all_u8_powers:?}");

    let collatz: Vec<u64> = successors(Some(6u64), |&n| {
        (n != 1).then(|| if n % 2 == 0 { n / 2 } else { 3 * n + 1 })
    })
    .collect();
    println!("collatz {collatz:?}");

    let digits: Vec<u32> = successors(Some(9045u32), |n| (*n >= 10).then_some(n / 10))
        .map(|n| n % 10)
        .collect();

    println!("digits reversed {digits:?}");
}

// Example 3: once, once_with, and empty.
fn demo_once_empty() {
    println!("-- once / empty --");

    let v: Vec<_> = once(1).chain(once(2)).chain(empty()).collect();
    println!("{v:?}");

    // Add a header row to a list without allocating a new Vec first.
    let rows = ["a", "b"];
    let with_header: Vec<_> = once("NAME").chain(rows).collect();
    println!("{with_header:?}");

    let lazy = once_with(|| {
        println!("  computing the one value");
        42
    });

    println!("before pulling");
    println!("got {:?}", lazy.collect::<Vec<_>>());

    let none: Vec<i32> = empty().collect();
    println!("empty {none:?}");
}

// Example 4: repeat, repeat_n, repeat_with.
fn demo_repeat() {
    println!("-- repeat --");

    println!("{:?}", repeat("x").take(3).collect::<Vec<_>>());
    println!("{:?}", repeat_n(0u8, 4).collect::<Vec<_>>());

    let line: String = repeat('=').take(10).collect();
    println!("{line}");

    let mut seed = 7u32;
    let pseudo: Vec<u32> = repeat_with(|| {
        seed = seed.wrapping_mul(1103515245).wrapping_add(12345);
        (seed >> 16) % 100
    })
    .take(5)
    .collect();
    println!("pseudo random {pseudo:?}");
}

// Example 5: returning sources from functions.
fn evens() -> impl Iterator<Item = u32> {
    (0..).step_by(2)
}

fn countdown(from: u32) -> impl Iterator<Item = u32> {
    successors(Some(from), |&n| n.checked_sub(1))
}

fn padded<'a>(items: &'a [&'a str], width: usize) -> impl Iterator<Item = &'a str> + 'a {
    items.iter().copied().chain(repeat("-")).take(width)
}

fn demo_returned_sources() {
    println!("-- returned sources --");

    println!("{:?}", evens().take(5).collect::<Vec<_>>());
    println!("{:?}", countdown(3).collect::<Vec<_>>());
    println!("{:?}", padded(&["a", "b"], 5).collect::<Vec<_>>());
}

// Example 6: windows over generated values.
fn demo_pairs() {
    println!("-- pairs from sources --");

    let squares = (1..).map(|n: u32| n * n);
    let gaps: Vec<_> =
        squares.clone().zip(squares.skip(1)).map(|(a, b)| b - a).take(5).collect();
    println!("gaps between squares {gaps:?}");

    let alternating: Vec<i32> =
        [1, -1].iter().copied().cycle().zip(1..=6).map(|(s, n)| s * n).collect();
    println!("alternating {alternating:?}");
}

fn main() {
    demo_from_fn();
    demo_successors();
    demo_once_empty();
    demo_repeat();
    demo_returned_sources();
    demo_pairs();
}