# (Result<T,E>, Result<T,E>, ...) -> Result<(T, T, ...), E>

What would be a not too painful way of turning a homogeneous tuple of `Result<T,E>` into a single `Result` containing a tuple of `Ok`s, or the first `Err`? That is to say

• `(Ok(a), Ok(b), Ok(3)) -> Ok((a, b, c))`
• `(Ok(a), Err(b), Ok(c)) -> Err(b)`
• `(Ok(a), Err(b), Err(c)) -> Err(b)`

In other words, I want Haskell's `sequence` (`traverse id`) on a (`Traversable`) tuple of `Either`s.

(The tuple will always have 3 elements.)

Except that tuples in Haskell aren’t traversable (in that way).

With `try` blocks, I’d suggest `try { (x1?, x2?, x3?) }`. I guess, the equivalent with `and_then` is `x1.and_then(|x1| x2.and_then(|x2| x3.map(|x3| (x1, x2, x3))))`.

Edit: In the meantime (without `try` blocks yet) you can also use a closure: `(|| Ok((x1?, x2?, x3?)))()`.

7 Likes

Or without `try` blocks, but with a suitable early-return context,

``````Ok((tuple.0?, tuple.1?, tuple.2?))
``````

E.g. since the error types are all the same, you could have

``````// There's definitely a better name
fn flatten<A, B, C, E>(tuple: (Result<A, E>, Result<B, E>, Result<C, E>)) -> Result<(A, B, C), E> {
Ok((tuple.0?, tuple.1?, tuple.2?))
}
``````

(It could be more generic for different error types too, but then you'd almost surely have to specify the end error type that could be made `From::from` every different error type.)

Or since you said homogeneous, even

``````// There's definitely a better name
fn flatten<O, E>(tuple: (Result<O, E>, Result<O, E>, Result<O, E>)) -> Result<(O, O, O), E> {
Ok((tuple.0?, tuple.1?, tuple.2?))
}
``````
2 Likes

Yes, I should simply have said 'list' instead of trying to maintain some link to the original tuple.

Actually, if it were a list (i.e. `Vec`) there is some higher-level way of doing it in Rust, isn't there? Don't the implementations of `*Iter*` for `Result` (and, presumably, `Option`) do exactly this?

The closure was actually my first attempt. When that didn't work I thought I'd ask. Still not sure what I did wrong. When I try to use it in the real-life context, I get all sorts of errors. But the equivalent named function with explicit type annotations gets me there.

Yes, for `Vec` you can do `vec.into_iter().collect()` and utilize this impl.

3 Likes

That's generally because `?` has multiple extension points for `Result`, so inference gets confused. (This will be one of the advantages of `try{}`, actually, assuming the change I'm working on goes in.)

If you specifically annotate the closure, like `(|| -> Result<_, TheActualErrorType> { Ok((x1?, x2?, x3?)) })()`, then it'll probably work. (Because the difference that matters about the "equivalent named function" is that you had to write out the return type on it.)

Yes, I guessed it would be something like that. By the time you write explicit annotations on the closure, it becomes cleaner, more manageable, etc. as as a function, so I'm OK with that.

Or even

``````fn flatten<O, E>((a, b, c): (Result<O, E>, Result<O, E>, Result<O, E>)) -> Result<(O, O, O), E> {
Ok((a?, b?, c?))
}
``````

using pattern matching rather than indexing into the tuple.

Yup, that's exactly what I ended up doing.