Skip to content

← Closures and Iterators step 17 of 28

Easy Primitives

from_fn and successors: iterators without writing a struct

Build two sequences using from_fn and successors, then concatenate them.

pub fn drive(script: Vec<u32>) -> Vec<u32>

Part A: follow the jumps

Start at index zero. If the index is in bounds, yield its value v, then advance by v + 1 positions. Stop once the index is outside the input. A zero value advances one position; it does not end the walk.

For [2, 9, 9, 3, 9, 9, 9, 1, 5], visit indices 0, 3 and 7, yielding [2, 3, 1]. The next index is 9, outside the vector.

Part B: generate successors

Use the last value from part A as the seed, or 1 if part A was empty. Always emit that seed. If the current value is less than 1000, emit current * 2 + 1 next. Otherwise stop.

The first value reaching or exceeding 1000 is included. A seed already at or above 1000 produces only itself. The seed is not removed from part A, so it appears again when part B begins.

Script Combined result
[] [1, 3, 7, 15, 31, 63, 127, 255, 511, 1023]
[0] [0, 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023]
[999] [999, 999, 1999]
[1000] [1000, 1000]
[u32::MAX] [u32::MAX, u32::MAX]

Inputs may contain any u32 value. The input and output fit in memory. Implement the walk with from_fn and the recurrence with successors.

Return an Option from the walk

from_fn calls an FnMut() -> Option<T> closure on each next() call. The starter returns a bare u32, causing E0308. Return Some(v) for a visited value and None when the index is out of bounds.

let v = *script.get(i)?; handles that lookup: get returns an Option, and ? returns None from the closure if there is no element. Advance the index without wrapping back into the input; saturating addition is useful because any jump past the last element must end the walk anyway.

from_fn does not itself promise permanent exhaustion. A later next() calls the closure again, even after it returned None. This walk stays exhausted because its index remains outside the vector. Use .fuse() when you need that guarantee for a closure that might later return Some.

Compute a successor only when needed

successors(Some(seed), ...) stores the first value and passes a reference to the current value into its closure to compute the next one. Return Some(next) below the threshold and None at or above it. Once exhausted, this iterator stays exhausted.

Use an explicit conditional for this recurrence:

fn next_value(x: u32) -> Option<u32> {
    if x < 1000 {
        Some(x * 2 + 1)
    } else {
        None
    }
}

bool::then_some(value) evaluates its argument even when the condition is false. Thus (x < 1000).then_some(x * 2 + 1) can overflow on a large seed that should immediately stop. then(|| value) is lazy; the explicit if above also makes the arithmetic guard clear.

The walk advances at least one position per yielded value. The recurrence increases only while its input is below 1000, so computed successors are at most 1999. Those bounds explain termination and keep the multiplication safe; x * 2 + 1 is not safe for every possible u32.

Your solution must compile, use both iterator constructors, match the output and pass Clippy with warnings treated as errors.