Rust needs evert(), which turns an iterator over Result of T into a Result of a collection of T or a collection of errors

currently the most ergonomic way to do this is to wrap a whole pipeline in a function boundary and use `?`.

@tindall I think collect() can do this? But it gets a bit tricky with type inference sometimes

@kookie yeah! But it doesn't collect the errors, it just gives up on the first one

I'm thinking of something like how Rails validations give you all the errors that came up at each step

@tindall Ooooh that's what you meant. Yea, I have written a bunch of fold combinators for errors before and wish the stdlib had something for this.

... maybe we should work on a PR together 👉👈

@tindall i guess from_iter() is out, then, since you want a collection of errors instead of just the first one

@QuietMisdreavus yeah that's the issue... this would be another nice use for higher kinded types, tbh, because you could do it with any collection of a type with left and right variants

@tindall ...huh. I wonder if it's possible to adjust collect to produce a Result<Vec<T>, Vec<E>>

@tindall something like this: maybe?

Just hacked this together, absolutely not efficient or anything, but ergonomic to use I guess...

@musicmatze @tindall Very nice! 👍

I slightly modified @musicmatze's example and made it generic over the "collectable" collection, so you can now collect into Vec, LinkedList etc.
It now basically works a bit like std `collect()`.


@musicmatze @tindall What's not optimal about the solution is, that we allocate a whole `Vec` first only to then call from_iter() in the end to turn it into our desired collection type, so we allocate two collections.

I've dabbled some more and come up with the following, probably more efficient, solution:


@musicmatze @tindall
The only downside now is that we have an additional requirement in our trait, which is:
it must implement `Extend`. However, when I remember correctly, every collection in std implements `Extend`, so this should be good enough.


@janriemer @tindall I guess the implementation can be simplified massively using Iterator::partition (

But I was too lazy to try it out.

@musicmatze @tindall Ah, thanks.
Not sure about the "simplified massively" part, though:
- partition requires that returned values implement Extend, so the additional trait bound needs to stay unfortunately
- we would need to move the bool "any_err" into the closure of `partition`, which would introduce a "side effect" -> imperative style then is probably more appropriate here
- also, there is an overhead of collecting Ok() values, although we might have Err() value already

@janriemer @tindall yes, it only simplifies the implementation, but does not make it more efficient or anything.

I wonder, though: If one wants to collect either a result or all errors, the results are processed anyways... so why not return them? This way the caller can decide what to do with them. Yes, this means more allocation, but also more ergonomics IMO.

@tindall mmmmmm, doesn't collect() handle that? I could swear librsvg does your first case.

@tindall Does this comment help you?

I think she is talking about this functionality which already exists. It is not very intuitive though.

let res = my_collection
.map(|x| method_that_returns_result(x))
.collect::<Result<Vec<T>, Error>>()?;

So it turns a Vec<Result, Error> into Result<Vec<T>, Error>, and you can turn it into Vec<T> simply with ?. Otherwise you would probably need a for loop.

Sign in to participate in the conversation

cybrespace: the social hub of the information superhighway jack in to the mastodon fediverse today and surf the dataflow through our cybrepunk, slightly glitchy web portal support us on patreon or liberapay!