globetrotter_model/ext.rs
1pub mod iter {
2 pub trait TryUnzipExt: Iterator {
3 /// Try to unzip an iterator of `Result<(A, B), E>` into two collections.
4 ///
5 /// On success, returns `(left, right)`; on the first `Err(e)`, returns `Err(e)`
6 /// and stops consuming the iterator.
7 ///
8 /// This mirrors `Iterator::unzip` in that the destination collections can be
9 /// any types implementing `Default + Extend`.
10 ///
11 /// # Errors
12 ///
13 /// Propagates the first error produced by the underlying iterator.
14 fn try_unzip<A, B, C1, C2, E>(self) -> Result<(C1, C2), E>
15 where
16 Self: Sized + Iterator<Item = Result<(A, B), E>>,
17 C1: Default + Extend<A>,
18 C2: Default + Extend<B>,
19 {
20 // we can’t generally reserve without specialization, so we keep it simple
21 let mut left = C1::default();
22 let mut right = C2::default();
23
24 for item in self {
25 let (a, b) = item?; // short-circuit on error
26 left.extend(std::iter::once(a));
27 right.extend(std::iter::once(b));
28 }
29 Ok((left, right))
30 }
31
32 /// Convenience helper that always returns `Vec`s.
33 ///
34 /// # Errors
35 ///
36 /// Propagates the first error produced by the underlying iterator.
37 fn try_unzip_vec<A, B, E>(self) -> Result<(Vec<A>, Vec<B>), E>
38 where
39 Self: Sized + Iterator<Item = Result<(A, B), E>>,
40 {
41 self.try_unzip::<A, B, Vec<A>, Vec<B>, E>()
42 }
43 }
44
45 impl<I: Iterator> TryUnzipExt for I {}
46}