aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/adaptors/coalesce.rs8
-rw-r--r--src/adaptors/map.rs8
-rw-r--r--src/adaptors/mod.rs30
-rw-r--r--src/adaptors/multi_product.rs8
-rw-r--r--src/duplicates_impl.rs23
-rw-r--r--src/free.rs37
-rw-r--r--src/impl_macros.rs2
-rw-r--r--src/kmerge_impl.rs2
-rw-r--r--src/lib.rs79
-rw-r--r--src/multipeek_impl.rs1
-rw-r--r--src/pad_tail.rs7
-rw-r--r--src/peeking_take_while.rs7
-rw-r--r--src/process_results_impl.rs17
-rw-r--r--src/tuple_impl.rs4
-rw-r--r--src/unziptuple.rs80
15 files changed, 292 insertions, 21 deletions
diff --git a/src/adaptors/coalesce.rs b/src/adaptors/coalesce.rs
index 1afbee5..b1aff6e 100644
--- a/src/adaptors/coalesce.rs
+++ b/src/adaptors/coalesce.rs
@@ -119,6 +119,10 @@ pub type DedupBy<I, Pred> = CoalesceBy<I, DedupPred2CoalescePred<Pred>, <I as It
#[derive(Clone)]
pub struct DedupPred2CoalescePred<DP>(DP);
+impl<DP> fmt::Debug for DedupPred2CoalescePred<DP> {
+ debug_fmt_fields!(DedupPred2CoalescePred,);
+}
+
pub trait DedupPredicate<T> {
// TODO replace by Fn(&T, &T)->bool once Rust supports it
fn dedup_pair(&mut self, a: &T, b: &T) -> bool;
@@ -137,7 +141,7 @@ where
}
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct DedupEq;
impl<T: PartialEq> DedupPredicate<T> for DedupEq {
@@ -186,7 +190,7 @@ where
pub type DedupByWithCount<I, Pred> =
CoalesceBy<I, DedupPredWithCount2CoalescePred<Pred>, (usize, <I as Iterator>::Item)>;
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct DedupPredWithCount2CoalescePred<DP>(DP);
impl<DP, T> CoalescePredicate<T, (usize, T)> for DedupPredWithCount2CoalescePred<DP>
diff --git a/src/adaptors/map.rs b/src/adaptors/map.rs
index eee5cc3..cf5e5a0 100644
--- a/src/adaptors/map.rs
+++ b/src/adaptors/map.rs
@@ -1,7 +1,7 @@
use std::iter::FromIterator;
use std::marker::PhantomData;
-#[derive(Clone)]
+#[derive(Clone, Debug)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct MapSpecialCase<I, F> {
iter: I,
@@ -84,6 +84,10 @@ where
#[derive(Clone)]
pub struct MapSpecialCaseFnOk<F>(F);
+impl<F> std::fmt::Debug for MapSpecialCaseFnOk<F> {
+ debug_fmt_fields!(MapSpecialCaseFnOk,);
+}
+
/// Create a new `MapOk` iterator.
pub fn map_ok<I, F, T, U, E>(iter: I, f: F) -> MapOk<I, F>
where
@@ -108,7 +112,7 @@ impl<T: Into<U>, U> MapSpecialCaseFn<T> for MapSpecialCaseFnInto<U> {
}
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct MapSpecialCaseFnInto<U>(PhantomData<U>);
/// Create a new [`MapInto`] iterator.
diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs
index bd0d93a..2010f53 100644
--- a/src/adaptors/mod.rs
+++ b/src/adaptors/mod.rs
@@ -477,7 +477,7 @@ pub trait MergePredicate<T> {
fn merge_pred(&mut self, a: &T, b: &T) -> bool;
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct MergeLte;
impl<T: PartialOrd> MergePredicate<T> for MergeLte {
@@ -849,6 +849,13 @@ pub struct FilterOk<I, F> {
f: F
}
+impl<I, F> fmt::Debug for FilterOk<I, F>
+where
+ I: fmt::Debug,
+{
+ debug_fmt_fields!(FilterOk, iter);
+}
+
/// Create a new `FilterOk` iterator.
pub fn filter_ok<I, F, T, E>(iter: I, f: F) -> FilterOk<I, F>
where I: Iterator<Item = Result<T, E>>,
@@ -917,6 +924,13 @@ pub struct FilterMapOk<I, F> {
f: F
}
+impl<I, F> fmt::Debug for FilterMapOk<I, F>
+where
+ I: fmt::Debug,
+{
+ debug_fmt_fields!(FilterMapOk, iter);
+}
+
fn transpose_result<T, E>(result: Result<Option<T>, E>) -> Option<Result<T, E>> {
match result {
Ok(Some(v)) => Some(Ok(v)),
@@ -995,6 +1009,13 @@ pub struct Positions<I, F> {
count: usize,
}
+impl<I, F> fmt::Debug for Positions<I, F>
+where
+ I: fmt::Debug,
+{
+ debug_fmt_fields!(Positions, iter, count);
+}
+
/// Create a new `Positions` iterator.
pub fn positions<I, F>(iter: I, f: F) -> Positions<I, F>
where I: Iterator,
@@ -1058,6 +1079,13 @@ pub struct Update<I, F> {
f: F,
}
+impl<I, F> fmt::Debug for Update<I, F>
+where
+ I: fmt::Debug,
+{
+ debug_fmt_fields!(Update, iter);
+}
+
/// Create a new `Update` iterator.
pub fn update<I, F>(iter: I, f: F) -> Update<I, F>
where
diff --git a/src/adaptors/multi_product.rs b/src/adaptors/multi_product.rs
index 9708ef4..30650ed 100644
--- a/src/adaptors/multi_product.rs
+++ b/src/adaptors/multi_product.rs
@@ -18,6 +18,14 @@ pub struct MultiProduct<I>(Vec<MultiProductIter<I>>)
where I: Iterator + Clone,
I::Item: Clone;
+impl<I> std::fmt::Debug for MultiProduct<I>
+where
+ I: Iterator + Clone + std::fmt::Debug,
+ I::Item: Clone + std::fmt::Debug,
+{
+ debug_fmt_fields!(CoalesceBy, 0);
+}
+
/// Create a new cartesian product iterator over an arbitrary number
/// of iterators of the same type.
///
diff --git a/src/duplicates_impl.rs b/src/duplicates_impl.rs
index 42049df..640d481 100644
--- a/src/duplicates_impl.rs
+++ b/src/duplicates_impl.rs
@@ -84,13 +84,18 @@ mod private {
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (_, hi) = self.iter.size_hint();
- // There are `hi` number of items left in the base iterator. In the best case scenario,
- // these items are exactly the same as the ones pending (i.e items seen exactly once so
- // far), plus (hi - pending) / 2 pairs of never seen before items.
let hi = hi.map(|hi| {
- let max_pending = std::cmp::min(self.meta.pending, hi);
- let max_new = std::cmp::max(hi - self.meta.pending, 0) / 2;
- max_pending + max_new
+ if hi <= self.meta.pending {
+ // fewer or equally many iter-remaining elements than pending elements
+ // => at most, each iter-remaining element is matched
+ hi
+ } else {
+ // fewer pending elements than iter-remaining elements
+ // => at most:
+ // * each pending element is matched
+ // * the other iter-remaining elements come in pairs
+ self.meta.pending + (hi - self.meta.pending) / 2
+ }
});
// The lower bound is always 0 since we might only get unique items from now on
(0, hi)
@@ -117,6 +122,7 @@ mod private {
}
/// Apply the identity function to elements before checking them for equality.
+ #[derive(Debug)]
pub struct ById;
impl<V> KeyMethod<V, V> for ById {
type Container = JustValue<V>;
@@ -128,6 +134,9 @@ mod private {
/// Apply a user-supplied function to elements before checking them for equality.
pub struct ByFn<F>(pub(crate) F);
+ impl<F> fmt::Debug for ByFn<F> {
+ debug_fmt_fields!(ByFn,);
+ }
impl<K, V, F> KeyMethod<K, V> for ByFn<F>
where
F: FnMut(&V) -> K,
@@ -147,6 +156,7 @@ mod private {
fn value(self) -> V;
}
+ #[derive(Debug)]
pub struct KeyValue<K, V>(K, V);
impl<K, V> KeyXorValue<K, V> for KeyValue<K, V> {
fn key_ref(&self) -> &K {
@@ -160,6 +170,7 @@ mod private {
}
}
+ #[derive(Debug)]
pub struct JustValue<V>(V);
impl<V> KeyXorValue<V, V> for JustValue<V> {
fn key_ref(&self) -> &V {
diff --git a/src/free.rs b/src/free.rs
index c78dc1d..6674030 100644
--- a/src/free.rs
+++ b/src/free.rs
@@ -14,8 +14,8 @@ use alloc::{
string::String,
};
-#[cfg(feature = "use_alloc")]
use crate::Itertools;
+use crate::intersperse::{Intersperse, IntersperseWith};
pub use crate::adaptors::{
interleave,
@@ -35,6 +35,41 @@ pub use crate::merge_join::merge_join_by;
#[cfg(feature = "use_alloc")]
pub use crate::rciter_impl::rciter;
+/// Iterate `iterable` with a particular value inserted between each element.
+///
+/// [`IntoIterator`] enabled version of [`Iterator::intersperse`].
+///
+/// ```
+/// use itertools::intersperse;
+///
+/// itertools::assert_equal(intersperse((0..3), 8), vec![0, 8, 1, 8, 2]);
+/// ```
+pub fn intersperse<I>(iterable: I, element: I::Item) -> Intersperse<I::IntoIter>
+ where I: IntoIterator,
+ <I as IntoIterator>::Item: Clone
+{
+ Itertools::intersperse(iterable.into_iter(), element)
+}
+
+/// Iterate `iterable` with a particular value created by a function inserted
+/// between each element.
+///
+/// [`IntoIterator`] enabled version of [`Iterator::intersperse_with`].
+///
+/// ```
+/// use itertools::intersperse_with;
+///
+/// let mut i = 10;
+/// itertools::assert_equal(intersperse_with((0..3), || { i -= 1; i }), vec![0, 9, 1, 8, 2]);
+/// assert_eq!(i, 8);
+/// ```
+pub fn intersperse_with<I, F>(iterable: I, element: F) -> IntersperseWith<I::IntoIter, F>
+ where I: IntoIterator,
+ F: FnMut() -> I::Item
+{
+ Itertools::intersperse_with(iterable.into_iter(), element)
+}
+
/// Iterate `iterable` with a running index.
///
/// [`IntoIterator`] enabled version of [`Iterator::enumerate`].
diff --git a/src/impl_macros.rs b/src/impl_macros.rs
index 3da8c63..5772bae 100644
--- a/src/impl_macros.rs
+++ b/src/impl_macros.rs
@@ -2,7 +2,7 @@
//! Implementation's internal macros
macro_rules! debug_fmt_fields {
- ($tyname:ident, $($($field:ident).+),*) => {
+ ($tyname:ident, $($($field:tt/*TODO ideally we would accept ident or tuple element here*/).+),*) => {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
f.debug_struct(stringify!($tyname))
$(
diff --git a/src/kmerge_impl.rs b/src/kmerge_impl.rs
index dce5b78..bd56b03 100644
--- a/src/kmerge_impl.rs
+++ b/src/kmerge_impl.rs
@@ -111,7 +111,7 @@ pub trait KMergePredicate<T> {
fn kmerge_pred(&mut self, a: &T, b: &T) -> bool;
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct KMergeByLt;
impl<T: PartialOrd> KMergePredicate<T> for KMergeByLt {
diff --git a/src/lib.rs b/src/lib.rs
index 7dd6241..df95e19 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -179,6 +179,7 @@ pub use crate::repeatn::repeat_n;
#[allow(deprecated)]
pub use crate::sources::{repeat_call, unfold, iterate};
pub use crate::with_position::Position;
+pub use crate::unziptuple::{multiunzip, MultiUnzip};
pub use crate::ziptuple::multizip;
mod adaptors;
mod either_or_both;
@@ -237,6 +238,7 @@ mod tuple_impl;
mod duplicates_impl;
#[cfg(feature = "use_std")]
mod unique_impl;
+mod unziptuple;
mod with_position;
mod zip_eq_impl;
mod zip_longest;
@@ -660,7 +662,7 @@ pub trait Itertools : Iterator {
}
/// Return an iterator over all contiguous windows producing tuples of
- /// a specific size (up to 4).
+ /// a specific size (up to 12).
///
/// `tuple_windows` clones the iterator elements so that they can be
/// part of successive windows, this makes it most suited for iterators
@@ -702,7 +704,7 @@ pub trait Itertools : Iterator {
/// Return an iterator over all windows, wrapping back to the first
/// elements when the window would otherwise exceed the length of the
- /// iterator, producing tuples of a specific size (up to 4).
+ /// iterator, producing tuples of a specific size (up to 12).
///
/// `circular_tuple_windows` clones the iterator elements so that they can be
/// part of successive windows, this makes it most suited for iterators
@@ -735,7 +737,7 @@ pub trait Itertools : Iterator {
tuple_impl::circular_tuple_windows(self)
}
/// Return an iterator that groups the items in tuples of a specific size
- /// (up to 4).
+ /// (up to 12).
///
/// See also the method [`.next_tuple()`](Itertools::next_tuple).
///
@@ -2241,6 +2243,7 @@ pub trait Itertools : Iterator {
/// assert_eq!((0..10).fold1(|x, y| x + y).unwrap_or(0), 45);
/// assert_eq!((0..0).fold1(|x, y| x * y), None);
/// ```
+ #[deprecated(since = "0.10.2", note = "Use `Iterator::reduce` instead")]
fn fold1<F>(mut self, f: F) -> Option<Self::Item>
where F: FnMut(Self::Item, Self::Item) -> Self::Item,
Self: Sized,
@@ -2665,6 +2668,43 @@ pub trait Itertools : Iterator {
v.into_iter()
}
+ /// Sort all iterator elements into a new iterator in ascending order. The key function is
+ /// called exactly once per key.
+ ///
+ /// **Note:** This consumes the entire iterator, uses the
+ /// [`slice::sort_by_cached_key`] method and returns the result as a new
+ /// iterator that owns its elements.
+ ///
+ /// The sorted iterator, if directly collected to a `Vec`, is converted
+ /// without any extra copying or allocation cost.
+ ///
+ /// ```
+ /// use itertools::Itertools;
+ ///
+ /// // sort people in descending order by age
+ /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 27)];
+ ///
+ /// let oldest_people_first = people
+ /// .into_iter()
+ /// .sorted_by_cached_key(|x| -x.1)
+ /// .map(|(person, _age)| person);
+ ///
+ /// itertools::assert_equal(oldest_people_first,
+ /// vec!["Jill", "Jack", "Jane", "John"]);
+ /// ```
+ /// ```
+ #[cfg(feature = "use_alloc")]
+ fn sorted_by_cached_key<K, F>(self, f: F) -> VecIntoIter<Self::Item>
+ where
+ Self: Sized,
+ K: Ord,
+ F: FnMut(&Self::Item) -> K,
+ {
+ let mut v = Vec::from_iter(self);
+ v.sort_by_cached_key(f);
+ v.into_iter()
+ }
+
/// Sort the k smallest elements into a new iterator, in ascending order.
///
/// **Note:** This consumes the entire iterator, and returns the result
@@ -2770,6 +2810,8 @@ pub trait Itertools : Iterator {
/// Return a `HashMap` of keys mapped to `Vec`s of values. Keys and values
/// are taken from `(Key, Value)` tuple pairs yielded by the input iterator.
///
+ /// Essentially a shorthand for `.into_grouping_map().collect::<Vec<_>>()`.
+ ///
/// ```
/// use itertools::Itertools;
///
@@ -2791,8 +2833,8 @@ pub trait Itertools : Iterator {
/// Return an `Iterator` on a `HashMap`. Keys mapped to `Vec`s of values. The key is specified
/// in the closure.
- /// Different to `into_group_map_by` because the key is still present. It is also more general.
- /// You can also fold the `group_map`.
+ ///
+ /// Essentially a shorthand for `.into_grouping_map_by(f).collect::<Vec<_>>()`.
///
/// ```
/// use itertools::Itertools;
@@ -3401,6 +3443,33 @@ pub trait Itertools : Iterator {
{
self.map(f).counts()
}
+
+ /// Converts an iterator of tuples into a tuple of containers.
+ ///
+ /// `unzip()` consumes an entire iterator of n-ary tuples, producing `n` collections, one for each
+ /// column.
+ ///
+ /// This function is, in some sense, the opposite of [`multizip`].
+ ///
+ /// ```
+ /// use itertools::Itertools;
+ ///
+ /// let inputs = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)];
+ ///
+ /// let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = inputs
+ /// .into_iter()
+ /// .multiunzip();
+ ///
+ /// assert_eq!(a, vec![1, 4, 7]);
+ /// assert_eq!(b, vec![2, 5, 8]);
+ /// assert_eq!(c, vec![3, 6, 9]);
+ /// ```
+ fn multiunzip<FromI>(self) -> FromI
+ where
+ Self: Sized + MultiUnzip<FromI>,
+ {
+ MultiUnzip::multiunzip(self)
+ }
}
impl<T: ?Sized> Itertools for T where T: Iterator { }
diff --git a/src/multipeek_impl.rs b/src/multipeek_impl.rs
index 93b0227..5917681 100644
--- a/src/multipeek_impl.rs
+++ b/src/multipeek_impl.rs
@@ -38,6 +38,7 @@ impl<I: Iterator> MultiPeek<I> {
/// Works exactly like `.next()` with the only difference that it doesn't
/// advance itself. `.peek()` can be called multiple times, to peek
/// further ahead.
+ /// When `.next()` is called, reset the peeking “cursor”.
pub fn peek(&mut self) -> Option<&I::Item> {
let ret = if self.index < self.buf.len() {
Some(&self.buf[self.index])
diff --git a/src/pad_tail.rs b/src/pad_tail.rs
index 03867cb..de57ee4 100644
--- a/src/pad_tail.rs
+++ b/src/pad_tail.rs
@@ -16,6 +16,13 @@ pub struct PadUsing<I, F> {
filler: F,
}
+impl<I, F> std::fmt::Debug for PadUsing<I, F>
+where
+ I: std::fmt::Debug,
+{
+ debug_fmt_fields!(PadUsing, iter, min, pos);
+}
+
/// Create a new **PadUsing** iterator.
pub fn pad_using<I, F>(iter: I, min: usize, filler: F) -> PadUsing<I, F>
where I: Iterator,
diff --git a/src/peeking_take_while.rs b/src/peeking_take_while.rs
index f9f2134..cd0945a 100644
--- a/src/peeking_take_while.rs
+++ b/src/peeking_take_while.rs
@@ -83,6 +83,13 @@ pub struct PeekingTakeWhile<'a, I: 'a, F>
f: F,
}
+impl<'a, I: 'a, F> std::fmt::Debug for PeekingTakeWhile<'a, I, F>
+where
+ I: Iterator + std::fmt::Debug,
+{
+ debug_fmt_fields!(PeekingTakeWhile, iter);
+}
+
/// Create a PeekingTakeWhile
pub fn peeking_take_while<I, F>(iter: &mut I, f: F) -> PeekingTakeWhile<I, F>
where I: Iterator,
diff --git a/src/process_results_impl.rs b/src/process_results_impl.rs
index 9da108b..44308f3 100644
--- a/src/process_results_impl.rs
+++ b/src/process_results_impl.rs
@@ -30,6 +30,23 @@ impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E>
fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.iter.size_hint().1)
}
+
+ fn fold<B, F>(mut self, init: B, mut f: F) -> B
+ where
+ Self: Sized,
+ F: FnMut(B, Self::Item) -> B,
+ {
+ let error = self.error;
+ self.iter
+ .try_fold(init, |acc, opt| match opt {
+ Ok(x) => Ok(f(acc, x)),
+ Err(e) => {
+ *error = Err(e);
+ Err(acc)
+ }
+ })
+ .unwrap_or_else(|e| e)
+ }
}
/// “Lift” a function of the values of an iterator so that it can process
diff --git a/src/tuple_impl.rs b/src/tuple_impl.rs
index ca8b97c..d914e03 100644
--- a/src/tuple_impl.rs
+++ b/src/tuple_impl.rs
@@ -11,7 +11,7 @@ use std::marker::PhantomData;
// hiding the implementation details of `TupleCollect`.
// See https://github.com/rust-itertools/itertools/issues/387
-/// Implemented for homogeneous tuples of size up to 4.
+/// Implemented for homogeneous tuples of size up to 12.
pub trait HomogeneousTuple
: TupleCollect
{}
@@ -77,7 +77,7 @@ impl<T> ExactSizeIterator for TupleBuffer<T>
/// An iterator that groups the items in tuples of a specific size.
///
/// See [`.tuples()`](crate::Itertools::tuples) for more information.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Tuples<I, T>
where I: Iterator<Item = T::Item>,
diff --git a/src/unziptuple.rs b/src/unziptuple.rs
new file mode 100644
index 0000000..f468f05
--- /dev/null
+++ b/src/unziptuple.rs
@@ -0,0 +1,80 @@
+/// Converts an iterator of tuples into a tuple of containers.
+///
+/// `unzip()` consumes an entire iterator of n-ary tuples, producing `n` collections, one for each
+/// column.
+///
+/// This function is, in some sense, the opposite of [`multizip`].
+///
+/// ```
+/// use itertools::multiunzip;
+///
+/// let inputs = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)];
+///
+/// let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = multiunzip(inputs);
+///
+/// assert_eq!(a, vec![1, 4, 7]);
+/// assert_eq!(b, vec![2, 5, 8]);
+/// assert_eq!(c, vec![3, 6, 9]);
+/// ```
+///
+/// [`multizip`]: crate::multizip
+pub fn multiunzip<FromI, I>(i: I) -> FromI
+where
+ I: IntoIterator,
+ I::IntoIter: MultiUnzip<FromI>,
+{
+ i.into_iter().multiunzip()
+}
+
+/// An iterator that can be unzipped into multiple collections.
+///
+/// See [`.multiunzip()`](crate::Itertools::multiunzip) for more information.
+pub trait MultiUnzip<FromI>: Iterator {
+ /// Unzip this iterator into multiple collections.
+ fn multiunzip(self) -> FromI;
+}
+
+macro_rules! impl_unzip_iter {
+ ($($T:ident => $FromT:ident),*) => (
+ #[allow(non_snake_case)]
+ impl<IT: Iterator<Item = ($($T,)*)>, $($T, $FromT: Default + Extend<$T>),* > MultiUnzip<($($FromT,)*)> for IT {
+ fn multiunzip(self) -> ($($FromT,)*) {
+ // This implementation mirrors the logic of Iterator::unzip as close as possible.
+ // Unfortunately a lot of the used api there is still unstable represented by
+ // the commented out parts that follow.
+ //
+ // https://doc.rust-lang.org/src/core/iter/traits/iterator.rs.html#2816-2844
+
+ let mut res = ($($FromT::default(),)*);
+ let ($($FromT,)*) = &mut res;
+
+ // Still unstable #72631
+ // let (lower_bound, _) = self.size_hint();
+ // if lower_bound > 0 {
+ // $($FromT.extend_reserve(lower_bound);)*
+ // }
+
+ self.fold((), |(), ($($T,)*)| {
+ // Still unstable #72631
+ // $( $FromT.extend_one($T); )*
+ $( $FromT.extend(std::iter::once($T)); )*
+ });
+ res
+ }
+ }
+ );
+}
+
+impl_unzip_iter!();
+impl_unzip_iter!(A => FromA);
+impl_unzip_iter!(A => FromA, B => FromB);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ, K => FromK);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ, K => FromK, L => FromL);