sharky-arrayvec 0.3.1

An array backed vector
Documentation
use core::fmt::Debug;
use core::mem::ManuallyDrop;

use crate::ArrayVec;

impl<const C: usize, T> IntoIterator for ArrayVec<C, T> {
    type IntoIter = ArrayVecIter<C, T>;
    type Item = T;

    #[inline]
    fn into_iter(self) -> Self::IntoIter {
        ArrayVecIter::new(self)
    }
}

/// Iterate over elements of an [`ArrayVec`].
///
/// ```rust
/// # use sharky_arrayvec::*;
///
/// let vec = ArrayVec::from_array([1, 2, 3]);
/// let mut iter = ArrayVecIter::new(vec);
///
/// assert_eq!(iter.next(), Some(1));
/// assert_eq!(iter.next(), Some(2));
/// assert_eq!(iter.next(), Some(3));
/// assert_eq!(iter.next(), None);
/// ```
#[must_use]
pub struct ArrayVecIter<const C: usize, T> {
    // NOTE: The vec can't be automatically dropped because the yielded elements would be
    // double-dropped.
    vec:   ManuallyDrop<ArrayVec<C, T>>,
    index: usize,
}

impl<const C: usize, T> Drop for ArrayVecIter<C, T> {
    #[inline]
    fn drop(&mut self) {
        let len = self.vec.len();

        // SAFETY:
        // - All elements up to self.vec.len are valid and initialized.
        // - Only non-yielded elements are dropped to prevent double dropping
        unsafe {
            self.vec.array[self.index..len].assume_init_drop();
        }
    }
}

impl<const C: usize, T> ArrayVecIter<C, T> {
    /// Create a new [`ArrayVec`].
    ///
    /// ```rust
    /// # use sharky_arrayvec::*;
    ///
    /// let vec = ArrayVec::from_array([1, 2, 3]);
    /// let mut iter = ArrayVecIter::new(vec);
    ///
    /// assert_eq!(iter.next(), Some(1));
    /// assert_eq!(iter.next(), Some(2));
    /// assert_eq!(iter.next(), Some(3));
    /// assert_eq!(iter.next(), None);
    /// ```
    #[inline]
    pub const fn new(vec: ArrayVec<C, T>) -> Self {
        Self {
            vec:   ManuallyDrop::new(vec),
            index: 0,
        }
    }
}

impl<const C: usize, T> Iterator for ArrayVecIter<C, T> {
    type Item = T;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        if self.index < self.vec.len() {
            // SAFETY:
            //
            // - The item is initialized because `self.index <= self.len()`
            // - It's okay to duplicate the item because `self.vec` has already been moved
            //   into the iterator; The only usable copy is what's returned.
            // - The returned value will be dropped, so nothing is leaked.
            let item = unsafe { self.vec.array[self.index].assume_init_read() };
            // NOTE: the index is less than the length; therefore, it's impossible for it to
            // be equal to usize::MAX, so checks would be redundant.
            self.index = self.index.wrapping_add(1);
            Some(item)
        } else {
            None
        }
    }
}

impl<const C: usize, T: Debug> Debug for ArrayVecIter<C, T> {
    #[inline]
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_tuple("ArrayVecIter").field(&*self.vec).finish()
    }
}

impl<const C: usize, T: Clone> Clone for ArrayVecIter<C, T> {
    #[inline]
    fn clone(&self) -> Self {
        Self {
            vec:   self.vec.clone(),
            index: self.index,
        }
    }
}

#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn debug() {
        let expected = "ArrayVecIter([2, 2])";
        let iter = ArrayVec::from_array([2; 2]).into_iter();
        let str = format!("{iter:?}");
        assert_eq!(expected, str);
    }

    #[test]
    fn iter_consuming() {
        let vec = ArrayVec::from_array([1u8, 2, 3]);
        let collected: Vec<_> = vec.into_iter().collect();
        assert_eq!(collected, [1, 2, 3]);
    }

    #[test]
    fn iter_drop_partial() {
        // Non-Copy type; verifies no double-free under Miri
        let vec = ArrayVec::from_array([String::from("a"), String::from("b"), String::from("c")]);
        let mut iter = vec.into_iter();
        assert_eq!(iter.next(), Some(String::from("a")));
        drop(iter); // "b" and "c" must be dropped here, "a" already consumed
    }

    #[test]
    fn iter_empty() {
        let vec: ArrayVec<4, u8> = ArrayVec::new();
        assert_eq!(vec.into_iter().next(), None);
    }

    #[test]
    fn iter_clone_independent() {
        let vec = ArrayVec::from_array([1u8, 2, 3]);
        let mut iter = vec.into_iter();
        iter.next();
        let mut iter2 = iter.clone();
        assert_eq!(iter.next(), Some(2));
        assert_eq!(iter2.next(), Some(2));
    }
}