Skip to content

Commit 59cb6f5

Browse files
bors[bot]cassiersg
andauthored
Merge #525
525: Add a chain! macro. r=jswrenn a=cassiersg This is proposal to add a `chain!` macro which creates an iterator running multiple iterators sequentially. It basically calls multiple times the standard `.chain()`. This macro is useful to avoid rightwards shift of the code when chaining many iterators. For the implementation and tests, I took inspiration on the `izip!` macro. Co-authored-by: Gaëtan Cassiers <gaetan.cassiers@uclouvain.be> Co-authored-by: Gaëtan Cassiers <cassiersg@users.noreply.github.com>
2 parents 853f064 + c75c407 commit 59cb6f5

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

src/lib.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,72 @@ macro_rules! izip {
346346
};
347347
}
348348

349+
#[macro_export]
350+
/// [Chain][`chain`] zero or more iterators together into one sequence.
351+
///
352+
/// The comma-separated arguments must implement [`IntoIterator`].
353+
/// The final argument may be followed by a trailing comma.
354+
///
355+
/// [`chain`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.chain
356+
/// [`IntoIterator`]: https://doc.rust-lang.org/std/iter/trait.IntoIterator.html
357+
///
358+
/// # Examples
359+
///
360+
/// [`iter::empty`]: https://doc.rust-lang.org/std/iter/fn.empty.html
361+
///
362+
/// Empty invocations of `chain!` expand to an invocation of [`iter::empty`]:
363+
/// ```
364+
/// # use std::iter;
365+
/// use itertools::chain;
366+
///
367+
/// let _: iter::Empty<()> = chain!();
368+
/// let _: iter::Empty<i8> = chain!();
369+
/// ```
370+
///
371+
/// Invocations of `chain!` with one argument expand to [`arg.into_iter()`][`IntoIterator`]:
372+
/// ```
373+
/// use std::{ops::Range, slice};
374+
/// use itertools::chain;
375+
/// let _: <Range<_> as IntoIterator>::IntoIter = chain!((2..6),); // trailing comma optional!
376+
/// let _: <&[_] as IntoIterator>::IntoIter = chain!(&[2, 3, 4]);
377+
/// ```
378+
///
379+
/// Invocations of `chain!` with multiple arguments [`.into_iter()`][`IntoIterator`] each
380+
/// argument, and then [`chain`] them together:
381+
/// ```
382+
/// use std::{iter::*, ops::Range, slice};
383+
/// use itertools::{assert_equal, chain};
384+
///
385+
/// // e.g., this:
386+
/// let with_macro: Chain<Chain<Once<_>, Take<Repeat<_>>>, slice::Iter<_>> =
387+
/// chain![once(&0), repeat(&1).take(2), &[2, 3, 5],];
388+
///
389+
/// // ...is equivalant to this:
390+
/// let with_method: Chain<Chain<Once<_>, Take<Repeat<_>>>, slice::Iter<_>> =
391+
/// once(&0)
392+
/// .chain(repeat(&1).take(2))
393+
/// .chain(&[2, 3, 5]);
394+
///
395+
/// assert_equal(with_macro, with_method);
396+
/// ```
397+
macro_rules! chain {
398+
() => {
399+
core::iter::empty()
400+
};
401+
($first:expr $(, $rest:expr )* $(,)?) => {
402+
{
403+
let iter = core::iter::IntoIterator::into_iter($first);
404+
$(
405+
let iter =
406+
core::iter::Iterator::chain(
407+
iter,
408+
core::iter::IntoIterator::into_iter($rest));
409+
)*
410+
iter
411+
}
412+
};
413+
}
414+
349415
/// An [`Iterator`] blanket implementation that provides extra adaptors and
350416
/// methods.
351417
///

tests/test_core.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::it::multizip;
1313
use crate::it::free::put_back;
1414
use crate::it::iproduct;
1515
use crate::it::izip;
16+
use crate::it::chain;
1617

1718
#[test]
1819
fn product2() {
@@ -87,6 +88,28 @@ fn multizip3() {
8788
}
8889
}
8990

91+
#[test]
92+
fn chain_macro() {
93+
let mut chain = chain!(2..3);
94+
assert!(chain.next() == Some(2));
95+
assert!(chain.next().is_none());
96+
97+
let mut chain = chain!(0..2, 2..3, 3..5i8);
98+
for i in 0..5i8 {
99+
assert_eq!(Some(i), chain.next());
100+
}
101+
assert!(chain.next().is_none());
102+
103+
let mut chain = chain!();
104+
assert_eq!(chain.next(), Option::<()>::None);
105+
}
106+
107+
#[test]
108+
fn chain2() {
109+
let _ = chain!(1.., 2..);
110+
let _ = chain!(1.., 2.., );
111+
}
112+
90113
#[test]
91114
fn write_to() {
92115
let xs = [7, 9, 8];

0 commit comments

Comments
 (0)