Repository: bluss/rust-itertools
Pull request #266 (2018-03-04 15:19:08)

Add flatten() free function and fix flatten tests
This restores our travis to working order by not using `.flatten()` directly, since it is now incompatible with Rust nightly.

We introduce a free function `flatten` — similar to many other `IntoIterator`-enabled free functions we already have: https://docs.rs/itertools/0.7.6/itertools/#functions

The benefit of `flatten` as a free function is that we can support it from Rust 1.12.0 (version req right now) and for all the forseeable future. In a future version of itertools, this free function will be "upgraded" to use the std `.flatten()` iterator. The free function **will remain** while we will remove `Itertools::flatten` (unless std removes their version) in a future version.

Users that have compatibility problems with `Itertools::flatten` and its clash with `Iterator::flatten`, they can either use `use itertools::Itertools; Itertools::flatten(` *expression* `)` **or** `use itertools::flatten; flatten(` *expression* `)`.


Patch:
diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs
index c51ac1c..08970d6 100644
--- a/src/adaptors/mod.rs
+++ b/src/adaptors/mod.rs
@@ -1045,10 +1045,31 @@ pub struct Flatten<I, J> {
     front: Option<J>,
 }
 
-/// Create a new `Flatten` iterator.
-pub fn flatten<I, J>(iter: I) -> Flatten<I, J> {
+/// Flatten an iteratble of iterables into a single iteration of all
+/// elements in the iterables.
+///
+/// This is more or less equivalent to `.flat_map` with an identity
+/// function.
+///
+/// This is an `IntoIterator`-enabled version of the [`.flatten()`][1] adaptor.
+///
+/// [1]: trait.Itertools.html#method.flatten.
+///
+/// ```
+/// use itertools::flatten;
+///
+/// let data = vec![vec![1, 2, 3], vec![4, 5, 6]];
+///
+/// itertools::assert_equal(flatten(&data),
+///                         &[1, 2, 3, 4, 5, 6]);
+/// ```
+pub fn flatten<I, J>(iter: I) -> Flatten<I::IntoIter, J>
+    where I: IntoIterator,
+          I::Item: IntoIterator<IntoIter=J, Item=J::Item>,
+          J: Iterator,
+{
     Flatten {
-        iter: iter,
+        iter: iter.into_iter(),
         front: None,
     }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 247e6fe..76412e7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -103,6 +103,7 @@ pub mod structs {
     pub use ziptuple::Zip;
 }
 pub use structs::*;
+pub use adaptors::flatten;
 pub use concat_impl::concat;
 pub use cons_tuples_impl::cons_tuples;
 pub use diff::diff_with;
@@ -1088,12 +1089,15 @@ pub trait Itertools : Iterator {
         pad_tail::pad_using(self, min, f)
     }
 
-    /// Unravel a nested iterator.
+    /// Flatten an iterator of iterables into a single iteration of all
+    /// elements in the iterables.
     ///
     /// This is more or less equivalent to `.flat_map` with an identity
     /// function.
     ///
-    /// ```
+    /// See also the [`flatten`](fn.flatten.html) function.
+    ///
+    /// ```ignore
     /// use itertools::Itertools;
     ///
     /// let data = vec![vec![1, 2, 3], vec![4, 5, 6]];


Test patch:
diff --git a/tests/quick.rs b/tests/quick.rs
index 6295a1d..cb85931 100644
--- a/tests/quick.rs
+++ b/tests/quick.rs
@@ -17,6 +17,7 @@ use itertools::{
     multizip,
     EitherOrBoth,
 };
+use itertools::flatten;
 use itertools::free::{
     cloned,
     enumerate,
@@ -604,12 +605,12 @@ quickcheck! {
     }
 
     fn equal_flatten(a: Vec<Option<i32>>) -> bool {
-        itertools::equal(a.iter().flatten(),
+        itertools::equal(flatten(&a),
                          a.iter().filter_map(|x| x.as_ref()))
     }
 
     fn equal_flatten_vec(a: Vec<Vec<u8>>) -> bool {
-        itertools::equal(a.iter().flatten(),
+        itertools::equal(flatten(&a),
                          a.iter().flat_map(|x| x))
     }
 
diff --git a/tests/test_core.rs b/tests/test_core.rs
index f0df210..7fc931c 100644
--- a/tests/test_core.rs
+++ b/tests/test_core.rs
@@ -7,6 +7,7 @@
 
 #[macro_use] extern crate itertools as it;
 
+use it::flatten;
 use it::Itertools;
 use it::interleave;
 use it::multizip;
@@ -224,7 +225,7 @@ fn flatten_clone() {
         &[1,2,3],
         &[4,5,6]
     ];
-    let flattened1 = data.into_iter().cloned().flatten();
+    let flattened1 = flatten(data.into_iter().cloned());
     let flattened2 = flattened1.clone();
 
     it::assert_equal(flattened1, &[1,2,3,4,5,6]);
diff --git a/tests/test_std.rs b/tests/test_std.rs
index 1f7a465..0341c61 100644
--- a/tests/test_std.rs
+++ b/tests/test_std.rs
@@ -2,6 +2,7 @@
 #[macro_use] extern crate itertools as it;
 extern crate permutohedron;
 
+use it::flatten;
 use it::Itertools;
 use it::multizip;
 use it::multipeek;
@@ -539,17 +540,15 @@ fn concat_non_empty() {
 
 #[test]
 fn flatten_iter() {
-    let data = vec![vec![1,2,3], vec![4,5,6]];
-    let flattened = data.into_iter().flatten();
-
-    it::assert_equal(flattened, vec![1,2,3,4,5,6]);
+    let data = vec![vec![1,2,3], vec![4,5], vec![], vec![6]];
+    it::assert_equal(flatten(data), vec![1,2,3,4,5,6]);
 }
 
 #[test]
 fn flatten_fold() {
     let xs = [0, 1, 1, 1, 2, 1, 3, 3];
     let ch = xs.iter().chunks(3);
-    let mut iter = ch.into_iter().flatten();
+    let mut iter = flatten(&ch);
     iter.next();
     let mut xs_d = Vec::new();
     iter.fold((), |(), &elt| xs_d.push(elt));