copy_stack_vec/
iter.rs

1// This file is part of copy-stack-vec.
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Iterator support for [`CopyStackVec`](crate::CopyStackVec).
5//!
6//! - `IntoIter<T, N>` yields by value and supports `DoubleEndedIterator`,
7//!   `ExactSizeIterator`, and `FusedIterator`.
8//! - `&CopyStackVec` and `&mut CopyStackVec` iterate as slices.
9
10#[cfg(not(feature = "unsafe-maybe-uninit"))]
11mod array;
12#[cfg(feature = "unsafe-maybe-uninit")]
13mod maybe_uninit;
14
15// Crate imports
16use crate::vec::CopyStackVec;
17
18// Core imports
19use core::iter::FusedIterator;
20
21/// Owned iterator returned by `CopyStackVec::into_iter()`.
22///
23/// Yields elements by value from front to back and supports double-ended
24/// iteration via [`DoubleEndedIterator`].
25pub struct IntoIter<T: Copy, const N: usize> {
26    pub(crate) v: CopyStackVec<T, N>,
27    pub(crate) front: usize,
28    pub(crate) back: usize, // exclusive
29}
30
31impl<T: Copy, const N: usize> Iterator for IntoIter<T, N> {
32    type Item = T;
33    fn next(&mut self) -> Option<T> {
34        if self.front < self.back {
35            let i = self.front;
36            self.front += 1;
37            Some(self.v.as_slice()[i])
38        } else {
39            None
40        }
41    }
42    fn size_hint(&self) -> (usize, Option<usize>) {
43        let rem = self.back - self.front;
44        (rem, Some(rem))
45    }
46    fn nth(&mut self, n: usize) -> Option<T> {
47        let rem = self.back - self.front;
48        if n >= rem {
49            self.front = self.back;
50            return None;
51        }
52        let i = self.front + n; // safe: n < rem == back - front
53        self.front = i + 1;
54        Some(self.v.as_slice()[i])
55    }
56}
57
58impl<T: Copy, const N: usize> DoubleEndedIterator for IntoIter<T, N> {
59    fn next_back(&mut self) -> Option<T> {
60        if self.front < self.back {
61            self.back -= 1;
62            Some(self.v.as_slice()[self.back])
63        } else {
64            None
65        }
66    }
67    fn nth_back(&mut self, n: usize) -> Option<T> {
68        let rem = self.back - self.front;
69        if n >= rem {
70            self.front = self.back;
71            None
72        } else {
73            self.back -= n + 1;
74            Some(self.v.as_slice()[self.back])
75        }
76    }
77}
78impl<T: Copy, const N: usize> FusedIterator for IntoIter<T, N> {}
79impl<T: Copy, const N: usize> ExactSizeIterator for IntoIter<T, N> {}
80
81impl<'a, T: Copy, const N: usize> IntoIterator for &'a CopyStackVec<T, N> {
82    type Item = &'a T;
83    type IntoIter = core::slice::Iter<'a, T>;
84    fn into_iter(self) -> Self::IntoIter {
85        self.as_slice().iter()
86    }
87}
88impl<'a, T: Copy, const N: usize> IntoIterator for &'a mut CopyStackVec<T, N> {
89    type Item = &'a mut T;
90    type IntoIter = core::slice::IterMut<'a, T>;
91    fn into_iter(self) -> Self::IntoIter {
92        self.as_mut_slice().iter_mut()
93    }
94}
95impl<T: Copy, const N: usize> IntoIterator for CopyStackVec<T, N> {
96    type Item = T;
97    type IntoIter = IntoIter<T, N>;
98    fn into_iter(self) -> Self::IntoIter {
99        IntoIter {
100            front: 0,
101            back: self.len,
102            v: self,
103        }
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    // Imports
110    use super::CopyStackVec;
111
112    #[test]
113    fn test_double_ended_and_nth() {
114        let v: CopyStackVec<i32, 6> = CopyStackVec::try_from(&[10, 20, 30, 40][..]).unwrap();
115        let mut it = v.into_iter();
116        assert_eq!(it.next(), Some(10));
117        assert_eq!(it.next_back(), Some(40));
118        assert_eq!(it.nth(1), Some(30));
119        assert_eq!(it.next(), None);
120    }
121
122    #[test]
123    fn test_into_iter_nth_back_sequence() {
124        let v: CopyStackVec<i32, 6> = CopyStackVec::try_from(&[1, 2, 3, 4, 5][..]).unwrap();
125        let mut it = v.into_iter();
126        assert_eq!(it.nth_back(0), Some(5));
127        assert_eq!(it.nth_back(1), Some(3)); // skip 1 from back, take 3
128        assert_eq!(it.next_back(), Some(2));
129        assert_eq!(it.next(), Some(1));
130        assert_eq!(it.next(), None);
131    }
132
133    #[test]
134    #[allow(clippy::iter_nth_zero)]
135    fn test_size_hint_tracks_consumption() {
136        let v: CopyStackVec<i32, 6> = CopyStackVec::try_from(&[10, 20, 30, 40][..]).unwrap();
137        let mut it = v.into_iter();
138        assert_eq!(it.size_hint(), (4, Some(4)));
139        assert_eq!(it.next(), Some(10));
140        assert_eq!(it.size_hint(), (3, Some(3)));
141        assert_eq!(it.next_back(), Some(40));
142        assert_eq!(it.size_hint(), (2, Some(2)));
143        assert_eq!(it.nth(0), Some(20));
144        assert_eq!(it.size_hint(), (1, Some(1)));
145        assert_eq!(it.next(), Some(30));
146        assert_eq!(it.size_hint(), (0, Some(0)));
147        assert_eq!(it.next(), None);
148    }
149
150    #[test]
151    #[allow(clippy::iter_nth_zero)]
152    fn test_nth_and_nth_back_boundary_conditions() {
153        let v: CopyStackVec<i32, 5> = CopyStackVec::try_from(&[1, 2, 3, 4, 5][..]).unwrap();
154        let mut it = v.into_iter();
155
156        // nth exactly remaining-1 returns last; nth >= remaining drains
157        assert_eq!(it.nth(3), Some(4)); // consumed [1,2,3], returns 4, remaining [5]
158        assert_eq!(it.nth(0), Some(5));
159        assert_eq!(it.nth(0), None);
160
161        let v2: CopyStackVec<i32, 5> = CopyStackVec::try_from(&[1, 2, 3, 4, 5][..]).unwrap();
162        let mut it2 = v2.into_iter();
163        assert_eq!(it2.nth_back(4), Some(1)); // exactly remaining-1 from back
164        assert_eq!(it2.next(), None);
165    }
166
167    #[test]
168    fn test_into_iter_zero_sized_type() {
169        let v: CopyStackVec<(), 3> = CopyStackVec::from([(); 3]);
170        let it = v.into_iter();
171        assert_eq!(it.size_hint(), (3, Some(3)));
172        assert_eq!(it.count(), 3);
173    }
174
175    #[test]
176    fn test_into_iter_zero_capacity() {
177        let v: CopyStackVec<u8, 0> = CopyStackVec::default();
178        let mut it = v.into_iter();
179        assert_eq!(it.next(), None);
180        assert_eq!(it.size_hint(), (0, Some(0)));
181    }
182
183    #[test]
184    fn test_nth_back_overflow_branch() {
185        let v: CopyStackVec<i32, 5> = CopyStackVec::try_from(&[10, 20, 30][..]).unwrap();
186        let mut it = v.into_iter();
187
188        // remaining = 3; n >= 3 → n = 3 or more will trigger overflow branch
189        assert_eq!(it.nth_back(3), None);
190
191        // Iterator must now be fully drained
192        assert_eq!(it.next(), None);
193        assert_eq!(it.next_back(), None);
194
195        // size_hint must be zero after draining
196        assert_eq!(it.size_hint(), (0, Some(0)));
197    }
198
199    #[test]
200    fn test_nth_back_exactly_remaining_branch() {
201        let v: CopyStackVec<i32, 4> = CopyStackVec::try_from(&[1, 2][..]).unwrap();
202        let mut it = v.into_iter();
203
204        // remaining = 2, so nth_back(2) should hit the n >= rem branch
205        assert_eq!(it.nth_back(2), None);
206
207        // fully drained
208        assert_eq!(it.next(), None);
209        assert_eq!(it.next_back(), None);
210    }
211}