Skip to content

Commit

Permalink
Merge #525
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
3 people committed Mar 1, 2021
2 parents 853f064 + c75c407 commit 59cb6f5
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 0 deletions.
66 changes: 66 additions & 0 deletions src/lib.rs
Expand Up @@ -346,6 +346,72 @@ macro_rules! izip {
};
}

#[macro_export]
/// [Chain][`chain`] zero or more iterators together into one sequence.
///
/// The comma-separated arguments must implement [`IntoIterator`].
/// The final argument may be followed by a trailing comma.
///
/// [`chain`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.chain
/// [`IntoIterator`]: https://doc.rust-lang.org/std/iter/trait.IntoIterator.html
///
/// # Examples
///
/// [`iter::empty`]: https://doc.rust-lang.org/std/iter/fn.empty.html
///
/// Empty invocations of `chain!` expand to an invocation of [`iter::empty`]:
/// ```
/// # use std::iter;
/// use itertools::chain;
///
/// let _: iter::Empty<()> = chain!();
/// let _: iter::Empty<i8> = chain!();
/// ```
///
/// Invocations of `chain!` with one argument expand to [`arg.into_iter()`][`IntoIterator`]:
/// ```
/// use std::{ops::Range, slice};
/// use itertools::chain;
/// let _: <Range<_> as IntoIterator>::IntoIter = chain!((2..6),); // trailing comma optional!
/// let _: <&[_] as IntoIterator>::IntoIter = chain!(&[2, 3, 4]);
/// ```
///
/// Invocations of `chain!` with multiple arguments [`.into_iter()`][`IntoIterator`] each
/// argument, and then [`chain`] them together:
/// ```
/// use std::{iter::*, ops::Range, slice};
/// use itertools::{assert_equal, chain};
///
/// // e.g., this:
/// let with_macro: Chain<Chain<Once<_>, Take<Repeat<_>>>, slice::Iter<_>> =
/// chain![once(&0), repeat(&1).take(2), &[2, 3, 5],];
///
/// // ...is equivalant to this:
/// let with_method: Chain<Chain<Once<_>, Take<Repeat<_>>>, slice::Iter<_>> =
/// once(&0)
/// .chain(repeat(&1).take(2))
/// .chain(&[2, 3, 5]);
///
/// assert_equal(with_macro, with_method);
/// ```
macro_rules! chain {
() => {
core::iter::empty()
};
($first:expr $(, $rest:expr )* $(,)?) => {
{
let iter = core::iter::IntoIterator::into_iter($first);
$(
let iter =
core::iter::Iterator::chain(
iter,
core::iter::IntoIterator::into_iter($rest));
)*
iter
}
};
}

/// An [`Iterator`] blanket implementation that provides extra adaptors and
/// methods.
///
Expand Down
23 changes: 23 additions & 0 deletions tests/test_core.rs
Expand Up @@ -13,6 +13,7 @@ use crate::it::multizip;
use crate::it::free::put_back;
use crate::it::iproduct;
use crate::it::izip;
use crate::it::chain;

#[test]
fn product2() {
Expand Down Expand Up @@ -87,6 +88,28 @@ fn multizip3() {
}
}

#[test]
fn chain_macro() {
let mut chain = chain!(2..3);
assert!(chain.next() == Some(2));
assert!(chain.next().is_none());

let mut chain = chain!(0..2, 2..3, 3..5i8);
for i in 0..5i8 {
assert_eq!(Some(i), chain.next());
}
assert!(chain.next().is_none());

let mut chain = chain!();
assert_eq!(chain.next(), Option::<()>::None);
}

#[test]
fn chain2() {
let _ = chain!(1.., 2..);
let _ = chain!(1.., 2.., );
}

#[test]
fn write_to() {
let xs = [7, 9, 8];
Expand Down

0 comments on commit 59cb6f5

Please sign in to comment.