Should we add `Iterator::try_collect()`?

TL;DR: Would it make sense to add a provided try_collect() method to Iterator?

fn try_collect<B, R>(&mut self) -> R where
    B: FromIterator<Self::Item>, 
    R: Try<Ok = B>,

Currently we only have try_fold() and try_for_each(). Over the past week I've been working on a networked state machine application where I end up processing accumulated messages that are allowed at a particular state. However, when extracting the data needed to proceed to the next stage and collecting it into a Vec, I want to generate errors for messages that don't match the current state. This means I've written a bunch of try_fold() versions of things that would be nicer with try_collect().

  1. The signature you are looking for would be:

    fn try_collect<R> (self) -> R
    where
        Self::Item : Try,
        R : Try<Error = <Self::Item as Try>::Error>,
        <R as Try>::Ok : FromIterator<<Self::Item as Try>::Ok>,
    ;    
    
  2. There is already impl<A, E, V: FromIterator<A>> FromIterator<Result<A, E>> for Result<V, E>

    • Although an Option or Try-generalised version does not exist yet, one can always use .map(|opt| opt.ok_or(())).collect().ok() (although I agree that these extra impls could definitely be added).
  3. Thus .try_collect() could be reserved for something different: collections that can be successfully collected into by only some kind of iterators, such as:

    • fixed-capacity collections such as array-based ones, which cannot be successfully .collected() into from an iterator that is too big (else there would be a silent truncation);

    • plain old arrays, which cannot be successfully .collected() into from an iterator that is not big enough.

    This would work in pair with a TryFromIterator trait for these kind of collections, and even a blanket impl based on ::core::convert::Infallible for FromIterator collections.

4 Likes

It exists for Option, too. And generalizing it for Try types requires HKTs, I think.

My bad, these docs are not always super easy to navigate :sweat_smile:

It does not . But it would require removing the current impls for Result and Option, or using specialization.

I think the current rule for specialization in the compiler is something along the lines of "for performance only: only simple entirely covered specialization that isn't visible from stable". I think that this use of specialization (default impl for <T: Try> plus impl for <Result>, impl for <Option>) is an acceptable specialization and could be added.

The major argument against would be that there's only the two impl Try types anyway, so the default code path would be unused (on stable).

Fair enough, I was talking about having Result::from_iterator require that the Item type of the iterator is Result as well (matching the current behavior), not just any other Try type that happens to have the correct Ok/Error types. But your implementation might be good enough since the types typically aren't inferred anyway when using FromIterator.

Oh, that's exactly what I wanted! I wonder if there is something we can do in the docs to make it more obvious that this works. Maybe just mention near the collect() examples that Error and Option implement FromIterator, too? Right now it's not very discoverable...