use crate::{mark_initialized, uninit_buf};
use core::iter::FromIterator;
pub struct ArrayFromIter<T, const N: usize>(pub Option<[T; N]>);
impl<T, const N: usize> FromIterator<T> for ArrayFromIter<T, N> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut buffer = uninit_buf::<T, N>();
let mut iter = iter.into_iter();
let mut buf_iter = buffer.iter_mut();
let mut num_written = 0;
loop {
let item = iter.next();
let slot = buf_iter.next();
match (item, slot) {
(Some(item), Some(slot)) => {
num_written += 1;
slot.write(item);
}
(Some(_), None) | (None, Some(_)) => {
buffer
.iter_mut()
.take(num_written)
.for_each(|slot| unsafe { slot.assume_init_drop() });
return Self(None);
}
(None, None) => return Self(Some(unsafe { mark_initialized(buffer) })),
};
}
}
}
#[cfg(test)]
mod tests {
use core::{
convert::TryInto,
sync::atomic::{AtomicUsize, Ordering},
};
use crate::testing::vec_strategy;
use proptest::{prop_assert, prop_assert_eq};
use test_strategy::proptest;
use super::*;
#[test]
fn can_collect_array_from_iter() {
let iter = vec![1, 2, 3].into_iter();
let ArrayFromIter(array) = iter.collect();
assert_eq!(array.unwrap(), [1, 2, 3]);
}
#[test]
fn fails_if_incorrect_number_of_elements() {
let iter = [1, 2, 3].iter();
let ArrayFromIter::<_, 4>(array) = iter.collect();
assert!(array.is_none());
let iter = [1, 2, 3].iter();
let ArrayFromIter::<_, 2>(array) = iter.collect();
assert!(array.is_none());
}
const LEN: usize = 100;
const SHORT_LEN: usize = LEN - 1;
const LONG_LEN: usize = LEN + 1;
#[derive(Clone)]
struct IncrementOnDrop<'a>(&'a AtomicUsize);
impl Drop for IncrementOnDrop<'_> {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::Relaxed);
}
}
#[test]
fn doesnt_leak_too_long() {
let drop_count = 0.into();
let ArrayFromIter::<_, 3>(_) = vec![IncrementOnDrop(&drop_count); 4].into_iter().collect();
assert_eq!(drop_count.load(Ordering::Relaxed), 4);
}
#[test]
fn doesnt_leak_too_short() {
let drop_count = 0.into();
let ArrayFromIter::<_, 3>(_) = vec![IncrementOnDrop(&drop_count); 2].into_iter().collect();
assert_eq!(drop_count.load(Ordering::Relaxed), 2);
}
#[proptest]
#[cfg_attr(miri, ignore)]
fn undersized_proptest(#[strategy(vec_strategy(LEN))] vec: Vec<String>) {
let ArrayFromIter::<String, SHORT_LEN>(array) = vec.into_iter().collect();
prop_assert!(array.is_none());
}
#[proptest]
#[cfg_attr(miri, ignore)]
fn oversized_proptest(#[strategy(vec_strategy(LEN))] vec: Vec<String>) {
let ArrayFromIter::<String, LONG_LEN>(array) = vec.into_iter().collect();
prop_assert!(array.is_none());
}
#[proptest]
#[cfg_attr(miri, ignore)]
fn just_right_proptest(#[strategy(vec_strategy(LEN))] vec: Vec<String>) {
let expected: [String; LEN] = vec.clone().try_into().unwrap();
let ArrayFromIter(array) = vec.into_iter().collect();
prop_assert_eq!(array.unwrap(), expected);
}
}