Conditionally iterate over one of several possible iterators

The most straightforward solution is to use a trait object:

use std::iter;

fn main() {
    let mut a;
    let mut b;

    let x: Option<i64> = None;

    // Repeat x 5 times if present, otherwise count from 1 to 5
    let iter: &mut dyn Iterator<Item = i64> = match x {
        None => {
            a = 1..5;
            &mut a
        }
        Some(x) => {
            b = iter::repeat(x).take(5);
            &mut b
        }
    };

    for i in iter {
        println!("{}", i);
    }
}

The main downside for this solution is that you have to allocate stack space for each concrete type you have. This also means variables for each type. A good thing is that only the used type needs to be initialized.

The same idea but requiring heap allocation is to use boxed trait objects:

use std::iter;

fn main() {
    let x: Option<i64> = None;

    // Repeat x 5 times if present, otherwise count from 1 to 5
    let iter: Box<dyn Iterator<Item = i64>> = match x {
        None => Box::new(1..5),
        Some(x) => Box::new(iter::repeat(x).take(5)),
    };

    for i in iter {
        println!("{}", i);
    }
}

This is mostly useful when you want to return the iterator from a function. The stack space taken is a single pointer, and only the needed heap space will be allocated.

You can also use an enum for each possible concrete iterator.

Leave a Comment