Skip to content

Commit

Permalink
Modify NullArray to support Array derive for unit structs
Browse files Browse the repository at this point in the history
  • Loading branch information
mbrobbel committed Nov 2, 2021
1 parent 1649196 commit e69d915
Showing 1 changed file with 93 additions and 11 deletions.
104 changes: 93 additions & 11 deletions src/array/null.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
use crate::{Array, ArrayIndex, ArrayType};
use std::iter::{self, Repeat, Take};
use std::{
iter::{self, Repeat, Take},
marker::PhantomData,
};

/// A sequence of nulls.
#[derive(Debug)]
pub struct NullArray {
///
/// This array type is also used as [ArrayType] when deriving [Array] for types
/// without fields (unit types). The generic `T` is used to provide iterator
/// implementations for array of these unit types.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct NullArray<T = ()> {
len: usize,
_ty: PhantomData<fn() -> T>,
}

impl<T> NullArray<T> {
/// Returns a new NullArray with the given length.
///
/// This never allocates.
pub fn with_len(len: usize) -> Self {
Self {
len,
_ty: PhantomData,
}
}

/// Returns the number of elements in the array, also referred to as its
/// length.
pub fn len(&self) -> usize {
self.len
}
}

impl Array for NullArray {
Expand Down Expand Up @@ -73,8 +99,11 @@ impl Array for NullArray {
}
}

impl ArrayIndex<usize> for NullArray {
type Output = ();
impl<T> ArrayIndex<usize> for NullArray<T>
where
T: Default,
{
type Output = T;

fn index(&self, index: usize) -> Self::Output {
#[cold]
Expand All @@ -87,29 +116,82 @@ impl ArrayIndex<usize> for NullArray {
if index >= len {
assert_failed(index, len);
}

T::default()
}
}

impl ArrayType for () {
type Array = NullArray;
}

impl FromIterator<()> for NullArray {
impl<T> FromIterator<T> for NullArray<T> {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = ()>,
I: IntoIterator<Item = T>,
{
Self {
len: iter.into_iter().count(),
_ty: PhantomData,
}
}
}

impl<'a> IntoIterator for &'a NullArray {
type Item = ();
type IntoIter = Take<Repeat<()>>;
impl<'a, T> IntoIterator for &'a NullArray<T>
where
T: Clone + Default,
{
type Item = T;
type IntoIter = Take<Repeat<T>>;

fn into_iter(self) -> Self::IntoIter {
iter::repeat(()).take(self.len)
iter::repeat(T::default()).take(self.len)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn from_iter() {
let vec = vec![(); 100];
let array = vec.into_iter().collect::<NullArray>();
assert_eq!(array.len(), 100);
assert!(array.is_null(0));
}

#[test]
fn into_iter() {
let vec = vec![(); 100];
let array = vec.iter().copied().collect::<NullArray>();
assert_eq!(vec, array.into_iter().collect::<Vec<_>>());
}

#[test]
fn unit_type() {
#[derive(Clone, Default, Debug, PartialEq)]
struct UnitStruct;

let vec = vec![UnitStruct; 100];
let array = vec.iter().cloned().collect::<NullArray<_>>();
assert_eq!(array.len(), 100);
assert_eq!(vec, array.into_iter().collect::<Vec<_>>());

#[derive(Clone, Debug, PartialEq)]
enum UnitEnum {
Unit,
}

impl Default for UnitEnum {
fn default() -> Self {
UnitEnum::Unit
}
}

let vec = vec![UnitEnum::default(); 100];
let array = vec.iter().cloned().collect::<NullArray<_>>();
assert_eq!(array.len(), 100);
assert_eq!(vec, array.into_iter().collect::<Vec<_>>());
}
}

0 comments on commit e69d915

Please sign in to comment.