Skip to content

Commit d92a05d

Browse files
IceTDrinkerjswrenn
authored andcommitted
feat: make izip! temporary friendly
- some izip! calls with temporary values would not compile with the current izip! implementation due to the use of a block to create the temporary iter chain, if a user called izip! with an iterator from a temporary value the compilation would fail - this change adapts the macro to stop making use of the block and calls $crate::__std_iter::Iterator::zip directly to avoid the need for temporary iter bindings, this means the recursion produces a tuple of the form (a, (b, (c, d))) instead of (((a, b), c), d), needing a slight adaptation of the closure arm to invert the way arguments are unpacked, it also adds two arms to recursively build the zipped iterators without the final map adapter as we don't process all iterators at once as before
1 parent 9238090 commit d92a05d

File tree

2 files changed

+68
-12
lines changed

2 files changed

+68
-12
lines changed

src/lib.rs

+22-12
Original file line numberDiff line numberDiff line change
@@ -321,14 +321,27 @@ macro_rules! iproduct {
321321
macro_rules! izip {
322322
// @closure creates a tuple-flattening closure for .map() call. usage:
323323
// @closure partial_pattern => partial_tuple , rest , of , iterators
324-
// eg. izip!( @closure ((a, b), c) => (a, b, c) , dd , ee )
324+
// eg. izip!( @closure (a, (b, c)) => (a, b, c) , dd , ee )
325325
( @closure $p:pat => $tup:expr ) => {
326326
|$p| $tup
327327
};
328328

329329
// The "b" identifier is a different identifier on each recursion level thanks to hygiene.
330330
( @closure $p:pat => ( $($tup:tt)* ) , $_iter:expr $( , $tail:expr )* ) => {
331-
$crate::izip!(@closure ($p, b) => ( $($tup)*, b ) $( , $tail )*)
331+
$crate::izip!(@closure (b, $p) => ( b, $($tup)* ) $( , $tail )*)
332+
};
333+
334+
// Inner recursion of the macro without final map adapter, base case
335+
( @ no_map @ $first:expr $(,)?) => {
336+
$crate::__std_iter::IntoIterator::into_iter($first)
337+
};
338+
339+
// Inner recursion of the macro without final map adapter, recursive case
340+
( @ no_map @ $first:expr, $($rest:expr),+ $(,)?) => {
341+
$crate::__std_iter::Iterator::zip(
342+
$crate::__std_iter::IntoIterator::into_iter($first),
343+
$crate::izip!(@ no_map @ $($rest),+)
344+
)
332345
};
333346

334347
// unary
@@ -346,16 +359,13 @@ macro_rules! izip {
346359

347360
// n-ary where n > 2
348361
( $first:expr $( , $rest:expr )* $(,)* ) => {
349-
{
350-
let iter = $crate::__std_iter::IntoIterator::into_iter($first);
351-
$(
352-
let iter = $crate::__std_iter::Iterator::zip(iter, $rest);
353-
)*
354-
$crate::__std_iter::Iterator::map(
355-
iter,
356-
$crate::izip!(@closure a => (a) $( , $rest )*)
357-
)
358-
}
362+
$crate::__std_iter::Iterator::map(
363+
$crate::__std_iter::Iterator::zip(
364+
$crate::__std_iter::IntoIterator::into_iter($first),
365+
$crate::izip!(@ no_map @ $($rest),+)
366+
),
367+
$crate::izip!(@closure a => (a) $( , $rest )*)
368+
)
359369
};
360370
}
361371

tests/zip.rs

+46
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,49 @@ fn test_double_ended_zip() {
5454
assert_eq!(it.next_back(), Some((1, 1)));
5555
assert_eq!(it.next_back(), None);
5656
}
57+
58+
#[test]
59+
/// The purpose of this test is not really to test the iterator mechanics itself, rather that it
60+
/// compiles and accepts temporary values as inputs, as those would be valid when not used with the
61+
/// izip! macro and zipped manually via .zip calls
62+
fn test_izip_with_temporaries() {
63+
struct Owned {
64+
data: Vec<i32>,
65+
}
66+
67+
impl Owned {
68+
fn new(val: i32) -> Self {
69+
Self {
70+
data: vec![val; 10],
71+
}
72+
}
73+
74+
fn as_view(&self) -> View<'_> {
75+
View {
76+
data: self.data.as_slice(),
77+
}
78+
}
79+
}
80+
81+
struct View<'a> {
82+
data: &'a [i32],
83+
}
84+
85+
impl View<'_> {
86+
fn iter(&self) -> impl Iterator<Item = &i32> {
87+
self.data.iter()
88+
}
89+
}
90+
91+
let a = Owned::new(0);
92+
let b = Owned::new(1);
93+
let c = Owned::new(2);
94+
95+
let mut sum = 0;
96+
97+
for (x, y, z) in itertools::izip!(a.as_view().iter(), b.as_view().iter(), c.as_view().iter()) {
98+
sum += x + y + z;
99+
}
100+
101+
assert_eq!(sum, 30);
102+
}

0 commit comments

Comments
 (0)