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}