Extract result from Iterator

I wanted to convert an Iterator of Result's into an Iterator of the inner values, but stopping at the first error and still having access to it, so threw together this code: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b3dcfad8212bd320dfee1f961a7f3aff .

My question is: Am I missing something built-in that does this already? If not, should/could the code be implemented in a better way?

PS: I'm aware of FromIterator, however I'd like to have an Iterator of inner values (not Result's) to work with while not having to collect them first. Perhaps I'm missing something obvious here.

Thanks.

You can collect iterator of Result<T, E> into Result<Vec<T>, E>. FromIterator is smart enough to flip it inside-out as needed.

["1", "fail", "3"].iter().map(i32::parse).collect::<Result<Vec<_>, _>()?

It will stop at the first error, but you won't have access to successful partial data.

If you want both, then fold can be used to collect data however you like it.

1 Like

This looks nice. I don't think there's any simple replacement for it in the standard library. The itertools crate has process_results which has a different design but might work for some of the same use cases.

You can also use take_while:

fn main() {
    let strings = &["10", "20", "30", "xyz", "50", "60"];

    let res: Option<std::num::ParseIntError> = None;
    let numbers: Vec<_> = strings
        .iter()
        .map(|s| s.parse::<i64>())
        .take_while(Result::is_ok)
        .map(Result::unwrap)
        .filter(|&n| n > 10)
        .collect();

    assert!(res.is_some());
    assert_eq!(vec![20, 30], numbers);
}

(Playground)

(Per @mbrubeck , this doesn’t meet requirements)

That doesn't save the error. (The first assert! fails.)

1 Like

My example code probably didn't illustrate the problem well, but I'd like to pass the iterator of non-Result values elsewhere, while still saving the error.

It doesn't fail here. Strange :frowning:

Here's a slightly modified version to illustrate without collecting the items.

https://play.rust-lang.org/?
version=stable&mode=debug&edition=2018&gist=5513b121e02d05c0e0fe54ef6b28d174

Here’s some updated code that does, but side effects in the take_while closure don’t sit well with me:

fn main() {
    let strings = &["10", "20", "30", "xyz", "50", "60"];

    let mut res: Option<std::num::ParseIntError> = None;
    let numbers: Vec<_> = strings
        .iter()
        .map(|s| s.parse::<i64>())
        .take_while(|r| match r {
            Ok(_) => true,
            Err(e) => { res = Some(e.clone()); false }
        })
        .map(Result::unwrap)
        .filter(|&n| n > 10)
        .collect();

    assert!(res.is_some());
    assert_eq!(vec![20, 30], numbers);
}
1 Like

Here's a version using map and Itertools::while_some: Playground.

(If you don't want the itertools dependency, you could replace the while_some with a combination of take_while and map, but it gets a bit noisy...)

1 Like

It would be great to have an adapter that did impl Iterator<Item = Result<T, E>>Result<impl Iterator<Item = T>, E>. But unfortunately that just can't be implemented elegantly.

The core problem is that you must iterate the whole iterator to find out whether it needs to be an Err, but for the iterator to produce them later, it needs to store those items somewhere, at which point this is just collect.

What you're doing looks like it's fairly similar to what collect-into-Result does internally:

But I don't think the library ever exposes that iterator directly, right now.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.