dyn_iter/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(
3    missing_docs,
4    missing_copy_implementations,
5    missing_debug_implementations,
6    missing_docs,
7    rust_2018_idioms
8)]
9use std::fmt::{Debug, Formatter};
10
11/// Iterator type that can wrap any kind of [`Iterator`].
12///
13/// This `struct` is a wrapper around types that implements `Iterator`
14/// trait. Since we do not know which specific type of `Iterator` is
15/// used, we `Box` it as a trait-object.
16///
17/// This iterator yields any type which usually depends on references on the
18/// model.  Therefore, the iterator must outlive the wrapped `Iterator`.
19///
20/// [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html
21pub struct DynIter<'iter, V> {
22    iter: Box<dyn Iterator<Item = V> + 'iter>,
23}
24
25impl<V> Debug for DynIter<'_, V> {
26    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
27        let size_str = match self.iter.size_hint() {
28            (min, None) => format!("at least {min}",),
29            (min, Some(max)) if min == max => format!("{min}",),
30            (min, Some(max)) => format!("between {min} and {max}"),
31        };
32        write!(f, "{{ iter: [Iterator with {size_str} elements]}}")
33    }
34}
35
36impl<'iter, V> DynIter<'iter, V> {
37    /// Instantiates an [`DynIter`] from any kind of [`Iterator`].
38    ///
39    /// [`DynIter`]: ./struct.DynIter.html
40    /// [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html
41    pub fn new<I>(iter: I) -> Self
42    where
43        I: Iterator<Item = V> + 'iter,
44    {
45        Self {
46            iter: Box::new(iter),
47        }
48    }
49}
50
51impl<'iter, V> Iterator for DynIter<'iter, V> {
52    type Item = V;
53    fn next(&mut self) -> Option<Self::Item> {
54        self.iter.next()
55    }
56
57    fn size_hint(&self) -> (usize, Option<usize>) {
58        self.iter.size_hint()
59    }
60
61    fn count(self) -> usize
62    where
63        Self: Sized,
64    {
65        self.iter.count()
66    }
67}
68
69/// Helper for easy conversion into a dynamic iterator `DynIter`.
70///
71/// ```
72/// # use dyn_iter::IntoDynIterator as _;
73/// let dyn_iter = vec![1, 2, 3, 4, 5]
74///     .into_iter()
75///     .filter(|x| x % 2 == 0)
76///     .map(|x| 3 * x + 1)
77///     .into_dyn_iter();
78/// assert_eq!(dyn_iter.count(), 2);
79/// ```
80pub trait IntoDynIterator: Iterator {
81    /// Helper function to convert an `Iterator` into a `DynIter`.
82    #[inline]
83    fn into_dyn_iter<'iter>(self) -> DynIter<'iter, Self::Item>
84    where
85        Self: Sized + 'iter,
86    {
87        DynIter::new(self)
88    }
89}
90
91impl<T: ?Sized> IntoDynIterator for T where T: Iterator {}
92
93#[cfg(test)]
94mod tests {
95    use crate::{DynIter, IntoDynIterator as _};
96
97    // This test is mostly checking that everything compiles and works as
98    // expected.
99    #[test]
100    fn it_compiles() {
101        let iter = (0..5).skip(2).filter(|n| *n != 3);
102        let mut dyn_iter = DynIter::new(iter);
103        assert_eq!(dyn_iter.size_hint(), (0, Some(3)));
104        assert_eq!(dyn_iter.next(), Some(2));
105        assert_eq!(dyn_iter.size_hint(), (0, Some(2)));
106        assert_eq!(dyn_iter.next(), Some(4));
107        assert_eq!(dyn_iter.size_hint(), (0, Some(0)));
108        assert_eq!(dyn_iter.next(), None);
109    }
110
111    #[test]
112    fn size_hint_count() {
113        let iter = (0..5).skip(2).filter(|e| *e != 3).into_dyn_iter();
114        assert_eq!(iter.size_hint(), (0, Some(3)));
115        assert_eq!(iter.count(), 2);
116    }
117
118    mod debug {
119        use super::*;
120
121        struct SizeHintIterator {
122            min: usize,
123            max: Option<usize>,
124        }
125
126        impl Iterator for SizeHintIterator {
127            type Item = u8;
128            fn next(&mut self) -> Option<Self::Item> {
129                unimplemented!()
130            }
131            fn size_hint(&self) -> (usize, Option<usize>) {
132                (self.min, self.max)
133            }
134        }
135
136        #[test]
137        fn no_max_size_hint() {
138            let iter = SizeHintIterator { min: 2, max: None };
139            let dyn_iter = DynIter::new(iter);
140            let debug_msg = format!("{dyn_iter:?}");
141            assert_eq!("{ iter: [Iterator with at least 2 elements]}", debug_msg);
142        }
143
144        #[test]
145        fn equal_min_max_size_hint() {
146            let iter = SizeHintIterator {
147                min: 3,
148                max: Some(3),
149            };
150            let dyn_iter = DynIter::new(iter);
151            let debug_msg = format!("{dyn_iter:?}");
152            assert_eq!("{ iter: [Iterator with 3 elements]}", debug_msg);
153        }
154
155        #[test]
156        fn different_min_max_size_hint() {
157            let iter = SizeHintIterator {
158                min: 4,
159                max: Some(5),
160            };
161            let dyn_iter = DynIter::new(iter);
162            let debug_msg = format!("{dyn_iter:?}");
163            assert_eq!(
164                "{ iter: [Iterator with between 4 and 5 elements]}",
165                debug_msg
166            );
167        }
168    }
169}