How do I perform iterator computations over iterators of Results without collecting to a temporary vector?

Iterator::try_fold provides the framework for what you need, and it’s available since Rust 1.27 (Playground):

fn fold_ok<I, T, E, F>(mut iter: I, f: F) -> Result<Option<T>, E>
where
    I: Iterator<Item = Result<T, E>>,
    T: Ord,
    F: Fn(T, T) -> T,
{
    iter.try_fold(None, |r, i| {
        let i = i?;
        Ok(Some(if let Some(r) = r { f(r, i) } else { i }))
    })
}

fn main() {
    let without_errors = vec![Ok(1), Ok(2), Ok(3)];
    let with_errors = vec![Ok(1), Err("error"), Ok(2)];

    fn doit<'r, T>(name: &str, iter: T)
    where
        T: Iterator<Item = &'r Result<i32, &'static str>> + Clone,
    {
        println!("{}: {:?}", name, fold_ok(iter.cloned(), ::std::cmp::min));
    }

    doit("without errors", without_errors.iter());
    doit("with errors", with_errors.iter());
}

Before that, I think your only option is manually iterating (Playground)

fn fold_ok<I, T, E, F>(mut iter: I, f: F) -> Result<Option<T>, E>
where
    I: Iterator<Item = Result<T, E>>,
    T: Ord,
    F: Fn(T, T) -> T,
{
    let mut result = match iter.next() {
        None => return Ok(None),
        Some(r) => r?,
    };

    for item in iter {
        result = f(result, item?);
    }

    Ok(Some(result))
}

fn main() {
    let without_errors = vec![Ok(1), Ok(2), Ok(3)];
    let with_errors = vec![Ok(1), Err("error"), Ok(2)];

    fn doit<'r, T>(name: &str, iter: T)
    where
        T: Iterator<Item = &'r Result<i32, &'static str>> + Clone,
    {
        println!(
            "{}: {:?}",
            name,
            fold_ok(iter.clone().cloned(), ::std::cmp::min)
        );
    }

    doit("without errors", without_errors.iter());
    doit("with errors", with_errors.iter());
}

Leave a Comment