rust-itertools / itertools

Extra iterator adaptors, iterator methods, free functions, and macros.
https://docs.rs/itertools/
Apache License 2.0
2.72k stars 309 forks source link

Proposal: `Iterator::try_flat_map()` method #969

Open Niedzwiedzw opened 3 months ago

Niedzwiedzw commented 3 months ago

Managed to get it to work with Box<_>, maybe it can be further optimized by defining a custom iterator. I use it a lot, it's very helpful

use std::iter::once;
use tap::prelude::*;

fn boxed_iter<'a, T: 'a>(
    iter: impl Iterator<Item = T> + 'a,
) -> Box<dyn Iterator<Item = T> + 'a> {
    Box::new(iter)
}

#[extension_traits::extension(pub trait IteratorTryFlatMapExt)]
impl<'iter, T, E, I> I
where
    E: 'iter,
    T: 'iter,
    I: Iterator<Item = std::result::Result<T, E>> + 'iter,
{
    fn try_flat_map<U, NewIterator, F>(
        self,
        mut try_flat_map: F,
    ) -> impl Iterator<Item = std::result::Result<U, E>> + 'iter
    where
        U: 'iter,
        NewIterator: Iterator<Item = std::result::Result<U, E>> + 'iter,
        F: FnMut(T) -> NewIterator + 'iter,
    {
        self.flat_map(move |e| match e {
            Ok(value) => value.pipe(&mut try_flat_map).pipe(boxed_iter),
            Err(e) => e.pipe(Err).pipe(once).pipe(boxed_iter),
        })
    }
}
Philippe-Cholet commented 3 months ago

I am a bit reluctant to add many methods for fallible iterators because it seems to open the door for many many cases. However, I reckon that this can not be easily written with Itertools::process_results? Something like that maybe

iter.process_results(|t_it| t_it.map(try_flat_map).process_results(|u_it| ...))??

You have an iterator of results then you map the "ok" values to another iterators of results, then this is flattened, am I right? It seems to do a lot.

Do you have an usage example for this?

Niedzwiedzw commented 3 months ago

process_results - I've never heard of it, I'll experiment with it, thanks! as for my use case, I needed something like this for recursively iterating a tree of elements and extracting each child is fallible