Try_collect as iter consuming operation


#1

Hi, I’m new to rust and still learn the ecosystem. The problem I have:

I have a Iterator<Option<T>> and want to collect it to Vec<T> if it only contains no None values. I think I want a function try_collect that transforms Iter<Option<T>> to Option<Vec<T>>. The name try_collect would be fitting, as there is a Option-unaware method collect and other fail-aware functions try_for_each and try_fold.

Is there anything like this in the standard library?

Currently, I create a list outside the iterator and insert the elements using try_for_each. Then, the collection is only returned if try_for_each was successful

fn try_collect(iterator: Iterator<Option<T>>) -> Option<Vec<T>> {
let mut ts = Vec::new();
let success = iterator.try_for_each(|t| t.map(|t| ts.push(t)))
match success {
    Some(()) => Some(ts)
    None => None
}
}

Any help is appreciated :slight_smile:


#2

Any reason for not using a slice? Iterators don’t have knowledge of what they contain until evaluated.


#3

I think collect should just work with an Option target:

fn main() {
    let not_all = vec![Some(2), Some(3), None];
    let all = vec![Some(2), Some(3), Some(4)];

    let collected_not_all: Option<Vec<_>> =
        not_all.into_iter().collect();
    let collected_all: Option<Vec<_>> =
        all.into_iter().collect();

    println!("not all: {:?}", collected_not_all);
    println!("all: {:?}", collected_all);
}

gives me

not all: None
all: Some([2, 3, 4])

Unless I’m missing something.

Edit: This is because you can collect into anything implementing FromIterator, and there is one for Option collection targets from iterators that produce Option values.


#4

Yes, collecting to Option<C> or Result<C, E> both have this behavior of collecting to any C with Some or Ok items, but stopping on the first None or Err.


#5

This keeps coming up on IRC, too. The FromIterator implementations for Option & Result seems to be one of those things that you cannot find unless you already know it exists.

I think it’d be worth having try_collect just for the clarity, documentability, and findability it would bring.

Edit: hmm, looks like futures has one: https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.1/futures_util/try_stream/trait.TryStreamExt.html#method.try_collect


#6

I’m not surprised many newcomers don’t find this for themselves. As a newbie myself I thought I would try to find it in the API documentation.

When you look at the API page for Option, it does list impl<A, V> FromIterator<Option<A>> for Option<V> under Trait Implementations. However in that section, the descriptions are hidden by default, you have to expand with the ‘+’ to see the details. Easy to overlook.