Skip to content

Commit

Permalink
Merge pull request #61 from novacrazy/master
Browse files Browse the repository at this point in the history
Small fixes and improvements before next release
  • Loading branch information
fizyk20 authored Mar 22, 2018
2 parents 38008a5 + 93c753d commit 5282a9d
Show file tree
Hide file tree
Showing 7 changed files with 342 additions and 30 deletions.
2 changes: 2 additions & 0 deletions src/functional.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
76 changes: 72 additions & 4 deletions src/iter.rs
Original file line number Diff line number Diff line change
@@ -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`
Expand All @@ -14,8 +14,34 @@ pub struct GenericArrayIter<T, N: ArrayLength<T>> {
index_back: usize,
}

unsafe impl<T: Send, N: ArrayLength<T>> Send for GenericArrayIter<T, N> {}
unsafe impl<T: Sync, N: ArrayLength<T>> Sync for GenericArrayIter<T, N> {}
#[cfg(test)]
mod test {
use super::*;

fn send<I: Send>(_iter: I) {}

#[test]
fn test_send_iter() {
send(GenericArray::from([1, 2, 3, 4]).into_iter());
}
}

impl<T, N> GenericArrayIter<T, N>
where
N: ArrayLength<T>,
{
/// 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<T, N> IntoIterator for GenericArray<T, N>
where
Expand All @@ -33,21 +59,59 @@ where
}
}

// Based on work in rust-lang/rust#49000
impl<T: fmt::Debug, N> fmt::Debug for GenericArrayIter<T, N>
where
N: ArrayLength<T>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("GenericArrayIter")
.field(&self.as_slice())
.finish()
}
}

impl<T, N> Drop for GenericArrayIter<T, N>
where
N: ArrayLength<T>,
{
#[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);
}
}
}
}

// Based on work in rust-lang/rust#49000
impl<T: Clone, N> Clone for GenericArrayIter<T, N>
where
N: ArrayLength<T>,
{
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<T, N> Iterator for GenericArrayIter<T, N>
where
N: ArrayLength<T>,
Expand Down Expand Up @@ -81,8 +145,10 @@ where
fn nth(&mut self, n: usize) -> Option<T> {
// 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);
}
Expand Down Expand Up @@ -120,3 +186,5 @@ where
self.index_back - self.index
}
}

// TODO: Implement `FusedIterator` and `TrustedLen` when stabilized
6 changes: 6 additions & 0 deletions src/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ pub unsafe trait GenericSequence<T>: 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<T> = <T as IntoIterator>::Item;

unsafe impl<'a, T: 'a, S: GenericSequence<T>> GenericSequence<T> for &'a S
where
&'a S: IntoIterator,
Expand Down
98 changes: 98 additions & 0 deletions tests/generics.rs
Original file line number Diff line number Diff line change
@@ -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: S)
where
S: FunctionalSequence<i32>, // `.map`
S::Item: Add<i32, Output = i32>, // `x + 1`
S: MappedGenericSequence<i32, i32>, // `i32` -> `i32`
MappedSequence<S, i32, i32>: 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, B>(a: A, b: B) -> i32
where
A: FunctionalSequence<i32>, // `.zip`
B: FunctionalSequence<i32, Length = A::Length>, // `.zip`
A: MappedGenericSequence<i32, i32>, // `i32` -> `i32`
B: MappedGenericSequence<i32, i32, Mapped = MappedSequence<A, i32, i32>>, // `i32` -> `i32`, prove A and B can map to the same output
A::Item: Add<B::Item, Output = i32>, // `l + r`
MappedSequence<A, i32, i32>: MappedGenericSequence<i32, i32> + FunctionalSequence<i32>, // `.map`
SequenceItem<MappedSequence<A, i32, i32>>: Add<i32, Output=i32>, // `x + 1`
MappedSequence<MappedSequence<A, i32, i32>, i32, i32>: Debug, // `println!`
MappedSequence<MappedSequence<A, i32, i32>, i32, i32>: FunctionalSequence<i32>, // `.fold`
SequenceItem<MappedSequence<MappedSequence<A, i32, i32>, i32, i32>>: Add<i32, Output=i32> // `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<i32, U4>, b: GenericArray<i32, U4>) -> 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<N>(a: GenericArray<i32, N>, b: GenericArray<i32, N>) -> i32
where
N: ArrayLength<i32>,
{
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<T, N>(a: GenericArray<T, N>, b: GenericArray<T, N>) -> i32
where
N: ArrayLength<T> + ArrayLength<<T as Add<T>>::Output>,
T: Add<T, Output=i32>,
{
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<A, B, N: ArrayLength<A> + ArrayLength<B>>(a: GenericArray<A, N>, b: GenericArray<B, N>) -> i32
where
A: Add<B>,
N: ArrayLength<<A as Add<B>>::Output> +
ArrayLength<<<A as Add<B>>::Output as Add<i32>>::Output>,
<A as Add<B>>::Output: Add<i32>,
<<A as Add<B>>::Output as Add<i32>>::Output: Add<i32, Output=i32>,
{
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);
}
Loading

0 comments on commit 5282a9d

Please sign in to comment.