rustiteratormultiplexing

How do you conditionally replace elements from one iterator with those from another in Rust


I'm looking for an iterator adapter that combines (multiplexes) the elements of two input iterators by conditionally replacing elements from the primary input iterator with those from the secondary input iterator, based on some predicate function.

For illustration I'm looking for an iterator kinda like this

struct MuxIter<A, B, F> {
    primary: A,   // primary input iterator
    secondary: B, // secondary input iterator
    predicate: F, // predicate that determines which input iterator to yield values from
}

impl<A, B, F, T> Iterator for MuxIter<A, B, F>
where
    A: Iterator<Item = T>,
    B: Iterator<Item = T>,
    F: Fn(&T) -> bool,
{
    type Item = T;

    #[rustfmt::skip]
    fn next(&mut self) -> Option<Self::Item> {
        let elem = self.primary.next()?; // First get the next element from the main iterator
        if (self.predicate)(&elem) {     // Check if it passes the predicate...
            Some(elem)                   // If it does, yeild it
        } else {
            self.secondary.next()        // if it doesn't, yeild the next element from the secondary iterator instead
        }
    }
}

See playground for full illustration of this in action.

Does such an iterator exist in std or in a 3rd party crate?


Solution

  • There is a facility in std that can be used to implement this: filter_map.

    fn mux_iter<'a, I1, I2, T, F>(
        primary: I1,
        secondary: I2,
        predicate: F,
    ) -> impl Iterator<Item = T> + 'a
    where
        I1: IntoIterator<Item = T>,
        I1::IntoIter: 'a,
        I2: IntoIterator<Item = T>,
        I2::IntoIter: 'a,
        F: Fn(&T) -> bool + 'a,
    {
        let mut secondary = secondary.into_iter();
        primary.into_iter().filter_map(move |v| {
            if predicate(&v) {
                Some(v)
            } else {
                secondary.next()
            }
        })
    }
    

    Note that there is a bug in your implementation: if the predicate returns false and the secondary iterator has no more items, your iterator will return None instead of continuing to the next element from the primary iterator. The filter_map-based iterator above does not have this bug.