dyn_slice/
iter.rs

1use core::{
2    iter::FusedIterator,
3    mem::transmute,
4    ptr::{metadata, DynMetadata, Pointee},
5};
6
7use crate::DynSlice;
8
9/// Dyn slice iterator
10pub struct Iter<'a, Dyn: ?Sized + Pointee<Metadata = DynMetadata<Dyn>>> {
11    pub(crate) slice: DynSlice<'a, Dyn>,
12}
13
14impl<'a, Dyn: ?Sized + Pointee<Metadata = DynMetadata<Dyn>> + 'a> Clone for Iter<'a, Dyn> {
15    fn clone(&self) -> Self {
16        Self { slice: self.slice }
17    }
18}
19
20impl<'a, Dyn: ?Sized + Pointee<Metadata = DynMetadata<Dyn>> + 'a> Iterator for Iter<'a, Dyn> {
21    type Item = &'a Dyn;
22
23    fn next(&mut self) -> Option<Self::Item> {
24        if self.slice.is_empty() {
25            None
26        } else {
27            // SAFETY:
28            // As the slice is not empty, it must have a first element and a valid vtable pointer, which
29            // can be transmuted to `DynMetadata<Dyn>`.
30            // The data is guaranteed to live for at least 'a, and not have a mutable reference to it
31            // in that time, so the lifetime can be extended.
32            let element: &'a Dyn = unsafe { transmute(self.slice.first_unchecked()) };
33
34            // SAFETY:
35            // As the slice is not empty, incrementing the pointer by one unit of the underlying type will
36            // yield either a valid pointer of the next element, or will yield a pointer one byte after the
37            // last element, which is valid as per [`core::ptr::const_ptr::add`]'s safety section.
38            self.slice.data = unsafe { self.slice.data.byte_add(metadata(element).size_of()) };
39            self.slice.len -= 1;
40
41            Some(element)
42        }
43    }
44
45    #[inline]
46    fn size_hint(&self) -> (usize, Option<usize>) {
47        let remaining = self.slice.len();
48        (remaining, Some(remaining))
49    }
50
51    #[inline]
52    fn count(self) -> usize {
53        self.slice.len()
54    }
55
56    fn nth(&mut self, n: usize) -> Option<Self::Item> {
57        if n >= self.slice.len() {
58            self.slice.len = 0;
59            return None;
60        }
61
62        // SAFETY:
63        // The above conditional guarantees that the slice is not empty and therefore has a valid vtable
64        // pointer, which can be transmuted to a `DynMetadata<Dyn>`.
65        let metadata: DynMetadata<Dyn> = unsafe { transmute(self.slice.vtable_ptr) };
66
67        // SAFETY:
68        // As `n < slice.len()`, adding `n` units of the underlying type to the pointer will yield a valid
69        // pointer in the slice.
70        self.slice.data = unsafe { self.slice.data.byte_add(metadata.size_of() * n) };
71        self.slice.len -= n;
72
73        self.next()
74    }
75
76    fn last(self) -> Option<Self::Item> {
77        // SAFETY:
78        // The data is guaranteed to live for at least 'a, and not have a mutable reference to it
79        // in that time, so the lifetime can be extended.
80        unsafe { transmute(self.slice.last()) }
81    }
82}
83
84impl<'a, Dyn: ?Sized + Pointee<Metadata = DynMetadata<Dyn>> + 'a> DoubleEndedIterator
85    for Iter<'a, Dyn>
86{
87    fn next_back(&mut self) -> Option<Self::Item> {
88        if self.slice.is_empty() {
89            None
90        } else {
91            let element: &'a Dyn =
92                // SAFETY:
93                // As the slice is not empty, it must have a last element (at `slice.len() - 1`) and a valid
94                // vtable pointer, which can be transmuted to `DynMetadata<Dyn>`.
95                // The data is guaranteed to live for at least 'a, and not have a mutable reference to it
96                // in that time, so the lifetime can be extended.
97                unsafe { transmute(self.slice.get_unchecked(self.slice.len - 1)) };
98
99            self.slice.len -= 1;
100
101            Some(element)
102        }
103    }
104
105    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
106        if n >= self.slice.len() {
107            self.slice.len = 0;
108            return None;
109        }
110
111        self.slice.len -= n;
112
113        self.next_back()
114    }
115}
116
117impl<'a, Dyn: ?Sized + Pointee<Metadata = DynMetadata<Dyn>> + 'a> ExactSizeIterator
118    for Iter<'a, Dyn>
119{
120    #[inline]
121    fn len(&self) -> usize {
122        self.slice.len()
123    }
124}
125
126impl<'a, Dyn: ?Sized + Pointee<Metadata = DynMetadata<Dyn>> + 'a> FusedIterator for Iter<'a, Dyn> {}
127
128#[cfg(test)]
129mod test {
130    use crate::standard::partial_eq;
131
132    #[test]
133    fn test_next() {
134        let array = [2, 3, 5, 7, 11];
135        let slice = partial_eq::new::<u8, _>(&array);
136
137        let mut iter = slice.iter();
138        for &expected in &array {
139            let actual = iter.next().expect("expected an element");
140            assert!(actual == &expected, "expected {expected}");
141        }
142    }
143
144    #[test]
145    fn test_size_hint() {
146        let array = [2, 3, 5, 7, 11];
147        let slice = partial_eq::new::<u8, _>(&array);
148
149        let mut iter = slice.iter();
150        for expected in (1..=array.len()).rev() {
151            let (lower, Some(upper)) = iter.size_hint() else {
152                panic!("expected an upper bound");
153            };
154
155            assert_eq!(lower, upper, "expected lower and upper bounds to be equal");
156            assert_eq!(
157                lower, expected,
158                "expected size hint to be {expected}, got {lower}"
159            );
160
161            let _ = iter.next().expect("expected an element");
162        }
163
164        let (lower, Some(upper)) = iter.size_hint() else {
165            panic!("expected an upper bound");
166        };
167
168        assert_eq!(lower, upper, "expected lower and upper bounds to be equal");
169        assert_eq!(lower, 0, "expected size hint to be 0, got {lower}");
170    }
171
172    #[test]
173    fn test_count() {
174        let array = [2, 3, 5, 7, 11];
175        let slice = partial_eq::new::<u8, _>(&array);
176
177        let mut iter = slice.iter();
178        for expected in (1..=array.len()).rev() {
179            let actual = iter.clone().count();
180            assert_eq!(
181                actual, expected,
182                "expected count to be {expected}, got {actual}"
183            );
184
185            let _ = iter.next().expect("expected an element");
186        }
187
188        let actual = iter.count();
189        assert_eq!(actual, 0, "expected count to be 0, got {actual}");
190    }
191
192    #[test]
193    fn test_nth() {
194        let array = [2, 3, 5, 7, 11];
195        let slice = partial_eq::new::<u8, _>(&array);
196
197        let mut iter = slice.iter();
198
199        #[allow(clippy::iter_nth_zero)]
200        let actual = iter.nth(0).expect("expected an element");
201        assert!(actual == &2, "expected 2");
202
203        assert!(
204            iter.nth(1).expect("expected an element") == &5,
205            "expected 5"
206        );
207        assert_eq!(iter.size_hint().0, 2, "expected 2 elements left");
208
209        assert!(iter.nth(2).is_none(), "expected none");
210        assert_eq!(iter.size_hint().0, 0, "expected 0 elements left");
211    }
212
213    #[test]
214    fn test_last() {
215        let array = [2, 3, 5, 7, 11];
216        let slice = partial_eq::new::<u8, _>(&array);
217
218        assert!(
219            slice.iter().last().expect("expected an element") == &11,
220            "expected 11"
221        );
222    }
223
224    #[test]
225    fn test_next_back() {
226        let array = [2, 3, 5, 7, 11];
227        let slice = partial_eq::new::<u8, _>(&array);
228
229        let mut iter = slice.iter();
230        for &expected in array.iter().rev() {
231            let actual = iter.next_back().expect("expected an element");
232            assert!(actual == &expected, "expected {expected}");
233        }
234    }
235
236    #[test]
237    fn test_nth_back() {
238        let array = [2, 3, 5, 7, 11];
239        let slice = partial_eq::new::<u8, _>(&array);
240
241        let mut iter = slice.iter();
242
243        #[allow(clippy::iter_nth_zero)]
244        let actual = iter.nth_back(0).expect("expected an element");
245        assert!(actual == &11, "expected 11");
246
247        assert!(
248            iter.nth_back(1).expect("expected an element") == &5,
249            "expected 5"
250        );
251        assert_eq!(iter.size_hint().0, 2, "expected 2 elements left");
252
253        assert!(iter.nth_back(2).is_none(), "expected none");
254        assert_eq!(iter.size_hint().0, 0, "expected 0 elements left");
255    }
256
257    #[test]
258    fn test_bidirectional() {
259        let array = [2, 3, 5, 7, 11];
260        let slice = partial_eq::new::<u8, _>(&array);
261
262        let mut iter = slice.iter();
263
264        assert!(
265            iter.next().expect("expected an element") == &2,
266            "expected 2"
267        );
268        assert_eq!(iter.size_hint().0, 4, "expected 4 elements left");
269
270        assert!(
271            iter.next_back().expect("expected an element") == &11,
272            "expected 11"
273        );
274        assert_eq!(iter.size_hint().0, 3, "expected 3 elements left");
275
276        assert!(
277            iter.nth(1).expect("expected an element") == &5,
278            "expected 5"
279        );
280        assert_eq!(iter.size_hint().0, 1, "expected 1 element left");
281
282        assert!(
283            iter.nth_back(0).expect("expected an element") == &7,
284            "expected 7"
285        );
286        assert_eq!(iter.size_hint().0, 0, "expected 0 elements left");
287    }
288}