From 8f5dc5d703d5cefecfde1a229eaf29d9e7117fc6 Mon Sep 17 00:00:00 2001 From: novacrazy Date: Wed, 14 Mar 2018 02:17:21 -0500 Subject: [PATCH 1/3] Improve examples in tests, add back `SequenceItem`, and fix `GenericArrayIter` Send/Sync. --- src/functional.rs | 2 + src/iter.rs | 13 ++++++- src/sequence.rs | 6 +++ tests/generics.rs | 98 +++++++++++++++++++++++++++++++++++++++++++++++ tests/std.rs | 21 ---------- 5 files changed, 117 insertions(+), 23 deletions(-) create mode 100644 tests/generics.rs delete mode 100644 tests/std.rs diff --git a/src/functional.rs b/src/functional.rs index d54f92bb80..50f4a71b8a 100644 --- a/src/functional.rs +++ b/src/functional.rs @@ -1,4 +1,6 @@ //! Functional programming with generic sequences +//! +//! Please see `tests/generics.rs` for examples of how to best use these in your generic functions. use super::ArrayLength; use core::iter::FromIterator; diff --git a/src/iter.rs b/src/iter.rs index d067d8dc61..ba3db2f55c 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -14,8 +14,17 @@ pub struct GenericArrayIter> { index_back: usize, } -unsafe impl> Send for GenericArrayIter {} -unsafe impl> Sync for GenericArrayIter {} +#[cfg(test)] +mod test { + use super::*; + + fn send(_iter: I) {} + + #[test] + fn test_send_iter() { + send(GenericArray::from([1, 2, 3, 4]).into_iter()); + } +} impl IntoIterator for GenericArray where diff --git a/src/sequence.rs b/src/sequence.rs index ff421d88f9..1b84ad38b6 100644 --- a/src/sequence.rs +++ b/src/sequence.rs @@ -69,6 +69,12 @@ pub unsafe trait GenericSequence: Sized + IntoIterator { } } +/// Accessor for `GenericSequence` item type, which is really `IntoIterator::Item` +/// +/// For deeply nested generic mapped sequence types, like shown in `tests/generics.rs`, +/// this can be useful for keeping things organized. +pub type SequenceItem = ::Item; + unsafe impl<'a, T: 'a, S: GenericSequence> GenericSequence for &'a S where &'a S: IntoIterator, diff --git a/tests/generics.rs b/tests/generics.rs new file mode 100644 index 0000000000..d48fe08c06 --- /dev/null +++ b/tests/generics.rs @@ -0,0 +1,98 @@ +#![recursion_limit = "128"] + +#[macro_use] +extern crate generic_array; + +use generic_array::typenum::consts::U4; + +use std::fmt::Debug; +use std::ops::Add; + +use generic_array::{GenericArray, ArrayLength}; +use generic_array::sequence::*; +use generic_array::functional::*; + +/// Example function using generics to pass N-length sequences and map them +pub fn generic_map(s: S) +where + S: FunctionalSequence, // `.map` + S::Item: Add, // `x + 1` + S: MappedGenericSequence, // `i32` -> `i32` + MappedSequence: Debug, // println! +{ + let a = s.map(|x| x + 1); + + println!("{:?}", a); +} + +/// Complex example function using generics to pass N-length sequences, zip them, and then map that result. +/// +/// If used with `GenericArray` specifically this isn't necessary +pub fn generic_sequence_zip_sum(a: A, b: B) -> i32 +where + A: FunctionalSequence, // `.zip` + B: FunctionalSequence, // `.zip` + A: MappedGenericSequence, // `i32` -> `i32` + B: MappedGenericSequence>, // `i32` -> `i32`, prove A and B can map to the same output + A::Item: Add, // `l + r` + MappedSequence: MappedGenericSequence + FunctionalSequence, // `.map` + SequenceItem>: Add, // `x + 1` + MappedSequence, i32, i32>: Debug, // `println!` + MappedSequence, i32, i32>: FunctionalSequence, // `.fold` + SequenceItem, i32, i32>>: Add // `x + a`, note the order +{ + let c = a.zip(b, |l, r| l + r).map(|x| x + 1); + + println!("{:?}", c); + + c.fold(0, |a, x| x + a) +} + +/// Super-simple fixed-length i32 `GenericArray`s +pub fn generic_array_plain_zip_sum(a: GenericArray, b: GenericArray) -> i32 { + a.zip(b, |l, r| l + r).map(|x| x + 1).fold(0, |a, x| x + a) +} + +pub fn generic_array_variable_length_zip_sum(a: GenericArray, b: GenericArray) -> i32 +where + N: ArrayLength, +{ + a.zip(b, |l, r| l + r).map(|x| x + 1).fold(0, |a, x| x + a) +} + +pub fn generic_array_same_type_variable_length_zip_sum(a: GenericArray, b: GenericArray) -> i32 +where + N: ArrayLength + ArrayLength<>::Output>, + T: Add, +{ + a.zip(b, |l, r| l + r).map(|x| x + 1).fold(0, |a, x| x + a) +} + +/// Complex example using fully generic `GenericArray`s with the same length. +/// +/// It's mostly just the repeated `Add` traits, which would be present in other systems anyway. +pub fn generic_array_zip_sum + ArrayLength>(a: GenericArray, b: GenericArray) -> i32 +where + A: Add, + N: ArrayLength<>::Output> + + ArrayLength<<>::Output as Add>::Output>, + >::Output: Add, + <>::Output as Add>::Output: Add, +{ + a.zip(b, |l, r| l + r).map(|x| x + 1).fold(0, |a, x| x + a) +} + +#[test] +fn test_generics() { + generic_map(arr![i32; 1, 2, 3, 4]); + + assert_eq!(generic_sequence_zip_sum(arr![i32; 1, 2, 3, 4], arr![i32; 2, 3, 4, 5]), 28); + + assert_eq!(generic_array_plain_zip_sum(arr![i32; 1, 2, 3, 4], arr![i32; 2, 3, 4, 5]), 28); + + assert_eq!(generic_array_variable_length_zip_sum(arr![i32; 1, 2, 3, 4], arr![i32; 2, 3, 4, 5]), 28); + + assert_eq!(generic_array_same_type_variable_length_zip_sum(arr![i32; 1, 2, 3, 4], arr![i32; 2, 3, 4, 5]), 28); + + assert_eq!(generic_array_zip_sum(arr![i32; 1, 2, 3, 4], arr![i32; 2, 3, 4, 5]), 28); +} \ No newline at end of file diff --git a/tests/std.rs b/tests/std.rs deleted file mode 100644 index 4f1a9e1cbf..0000000000 --- a/tests/std.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![recursion_limit = "128"] - -extern crate generic_array; - -use generic_array::functional::*; -//use generic_array::GenericArray; -use generic_array::sequence::*; -use std::fmt::Debug; -use std::ops::Add; - -pub fn test_generic(s: S) -where - S: FunctionalSequence, // `.map` - S::Item: Add, // `+` - S: MappedGenericSequence, // `i32` -> `i32` - MappedSequence: Debug, // println! -{ - let a = s.map(|x| x + 1); - - println!("{:?}", a); -} From 6cec8439a176097096965e5cab35abce7bcc7d82 Mon Sep 17 00:00:00 2001 From: novacrazy Date: Thu, 15 Mar 2018 16:44:28 -0500 Subject: [PATCH 2/3] Port some improvements from rust-lang/rust#49000 --- src/iter.rs | 62 ++++++++++++++++++- tests/iter.rs | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/mod.rs | 5 -- 3 files changed, 224 insertions(+), 7 deletions(-) create mode 100644 tests/iter.rs diff --git a/src/iter.rs b/src/iter.rs index ba3db2f55c..a90ad0016b 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -1,7 +1,7 @@ //! `GenericArray` iterator implementation. use super::{ArrayLength, GenericArray}; -use core::{cmp, ptr}; +use core::{cmp, ptr, fmt, mem}; use core::mem::ManuallyDrop; /// An iterator that moves out of a `GenericArray` @@ -26,6 +26,23 @@ mod test { } } +impl GenericArrayIter +where + N: ArrayLength, +{ + /// Returns the remaining items of this iterator as a slice + #[inline] + pub fn as_slice(&self) -> &[T] { + &self.array.as_slice()[self.index..self.index_back] + } + + /// Returns the remaining items of this iterator as a mutable slice + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [T] { + &mut self.array.as_mut_slice()[self.index..self.index_back] + } +} + impl IntoIterator for GenericArray where N: ArrayLength, @@ -42,6 +59,18 @@ where } } +// Based on work in rust-land/rust#49000 +impl fmt::Debug for GenericArrayIter +where + N: ArrayLength, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("GenericArrayIter") + .field(&self.as_slice()) + .finish() + } +} + impl Drop for GenericArrayIter where N: ArrayLength, @@ -49,7 +78,7 @@ where #[inline] fn drop(&mut self) { // Drop values that are still alive. - for p in &mut self.array[self.index..self.index_back] { + for p in self.as_mut_slice() { unsafe { ptr::drop_in_place(p); } @@ -57,6 +86,31 @@ where } } +impl Clone for GenericArrayIter +where + N: ArrayLength, +{ + fn clone(&self) -> Self { + // This places all cloned elements at the start of the new array iterator, + // not at their original indices. + unsafe { + let mut iter = GenericArrayIter { + array: ManuallyDrop::new(mem::uninitialized()), + index: 0, + index_back: 0, + }; + + for (dst, src) in iter.array.iter_mut().zip(self.as_slice()) { + ptr::write(dst, src.clone()); + + iter.index_back += 1; + } + + iter + } + } +} + impl Iterator for GenericArrayIter where N: ArrayLength, @@ -90,8 +144,10 @@ where fn nth(&mut self, n: usize) -> Option { // First consume values prior to the nth. let ndrop = cmp::min(n, self.len()); + for p in &mut self.array[self.index..self.index + ndrop] { self.index += 1; + unsafe { ptr::drop_in_place(p); } @@ -129,3 +185,5 @@ where self.index_back - self.index } } + +// TODO: Implement `FusedIterator` and `TrustedLen` when stabilized \ No newline at end of file diff --git a/tests/iter.rs b/tests/iter.rs new file mode 100644 index 0000000000..47860d728b --- /dev/null +++ b/tests/iter.rs @@ -0,0 +1,164 @@ +#[macro_use] +extern crate generic_array; + +use std::cell::Cell; +use std::ops::Drop; + +use generic_array::GenericArray; +use generic_array::typenum::consts::U5; + +#[test] +fn test_into_iter_as_slice() { + let array = arr![char; 'a', 'b', 'c']; + let mut into_iter = array.into_iter(); + assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); + let _ = into_iter.next().unwrap(); + assert_eq!(into_iter.as_slice(), &['b', 'c']); + let _ = into_iter.next().unwrap(); + let _ = into_iter.next().unwrap(); + assert_eq!(into_iter.as_slice(), &[]); +} + +#[test] +fn test_into_iter_as_mut_slice() { + let array = arr![char; 'a', 'b', 'c']; + let mut into_iter = array.into_iter(); + assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); + into_iter.as_mut_slice()[0] = 'x'; + into_iter.as_mut_slice()[1] = 'y'; + assert_eq!(into_iter.next().unwrap(), 'x'); + assert_eq!(into_iter.as_slice(), &['y', 'c']); +} + +#[test] +fn test_into_iter_debug() { + let array = arr![char; 'a', 'b', 'c']; + let into_iter = array.into_iter(); + let debug = format!("{:?}", into_iter); + assert_eq!(debug, "GenericArrayIter(['a', 'b', 'c'])"); +} + +#[test] +fn test_into_iter_clone() { + fn iter_equal>(it: I, slice: &[i32]) { + let v: Vec = it.collect(); + assert_eq!(&v[..], slice); + } + let mut it = arr![i32; 1, 2, 3].into_iter(); + iter_equal(it.clone(), &[1, 2, 3]); + assert_eq!(it.next(), Some(1)); + let mut it = it.rev(); + iter_equal(it.clone(), &[3, 2]); + assert_eq!(it.next(), Some(3)); + iter_equal(it.clone(), &[2]); + assert_eq!(it.next(), Some(2)); + iter_equal(it.clone(), &[]); + assert_eq!(it.next(), None); +} + +#[test] +fn test_into_iter_nth() { + let v = arr![i32; 0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(v.clone().into_iter().nth(i).unwrap(), v[i]); + } + assert_eq!(v.clone().into_iter().nth(v.len()), None); + + let mut iter = v.into_iter(); + assert_eq!(iter.nth(2).unwrap(), v[2]); + assert_eq!(iter.nth(1).unwrap(), v[4]); +} + +#[test] +fn test_into_iter_last() { + let v = arr![i32; 0, 1, 2, 3, 4]; + assert_eq!(v.into_iter().last().unwrap(), 4); + assert_eq!(arr![i32; 0].into_iter().last().unwrap(), 0); +} + +#[test] +fn test_into_iter_count() { + let v = arr![i32; 0, 1, 2, 3, 4]; + assert_eq!(v.clone().into_iter().count(), 5); + + let mut iter2 = v.into_iter(); + iter2.next(); + iter2.next(); + assert_eq!(iter2.count(), 3); +} + +#[test] +fn test_into_iter_flat_map() { + assert!((0..5).flat_map(|i| arr![i32; 2 * i, 2 * i + 1]).eq(0..10)); +} + +#[test] +fn test_into_iter_drops() { + struct R<'a> { + i: &'a Cell, + } + + impl<'a> Drop for R<'a> { + fn drop(&mut self) { + self.i.set(self.i.get() + 1); + } + } + + fn r(i: &Cell) -> R { + R { + i: i + } + } + + fn v(i: &Cell) -> GenericArray { + arr![R; r(i), r(i), r(i), r(i), r(i)] + } + + let i = Cell::new(0); + { + v(&i).into_iter(); + } + assert_eq!(i.get(), 5); + + let i = Cell::new(0); + { + let mut iter = v(&i).into_iter(); + let _x = iter.next(); + assert_eq!(i.get(), 0); + assert_eq!(iter.count(), 4); + assert_eq!(i.get(), 4); + } + assert_eq!(i.get(), 5); + + let i = Cell::new(0); + { + let mut iter = v(&i).into_iter(); + let _x = iter.nth(2); + assert_eq!(i.get(), 2); + let _y = iter.last(); + assert_eq!(i.get(), 3); + } + assert_eq!(i.get(), 5); + + let i = Cell::new(0); + for (index, _x) in v(&i).into_iter().enumerate() { + assert_eq!(i.get(), index); + } + assert_eq!(i.get(), 5); + + let i = Cell::new(0); + for (index, _x) in v(&i).into_iter().rev().enumerate() { + assert_eq!(i.get(), index); + } + assert_eq!(i.get(), 5); +} + +/* +//TODO: Cover this +#[allow(dead_code)] +fn assert_covariance() { + fn into_iter<'new>(i: GenericArrayIter<&'static str, U10>) -> GenericArrayIter<&'new str, U10> { + i + } +} +*/ \ No newline at end of file diff --git a/tests/mod.rs b/tests/mod.rs index 57359f8058..6c6d9eb492 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -57,11 +57,6 @@ fn test_copy() { assert_eq!(test2[0], 1); } -#[test] -fn test_iter_flat_map() { - assert!((0..5).flat_map(|i| arr![i32; 2 * i, 2 * i + 1]).eq(0..10)); -} - #[derive(Debug, PartialEq, Eq)] struct NoClone(T); From 93c753de9e88112939db99218eeaef05885a1c94 Mon Sep 17 00:00:00 2001 From: novacrazy Date: Thu, 15 Mar 2018 16:48:38 -0500 Subject: [PATCH 3/3] Fix typo --- src/iter.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/iter.rs b/src/iter.rs index a90ad0016b..a2d67fc679 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -59,7 +59,7 @@ where } } -// Based on work in rust-land/rust#49000 +// Based on work in rust-lang/rust#49000 impl fmt::Debug for GenericArrayIter where N: ArrayLength, @@ -86,6 +86,7 @@ where } } +// Based on work in rust-lang/rust#49000 impl Clone for GenericArrayIter where N: ArrayLength,