forward_iter/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3#![warn(clippy::pedantic)]
4
5use core::{fmt::Debug, iter::FusedIterator};
6
7use alloc::vec::{IntoIter, Vec};
8
9extern crate alloc;
10
11/// Create from [`ForwardIterExt::forward`]
12#[must_use]
13#[derive(Debug, Clone)]
14pub struct ForwardIter<I: Iterator> {
15    iter: Option<I>,
16    buf: IntoIter<I::Item>,
17}
18
19impl<I: Iterator> ForwardIter<I> {
20    fn backward(&mut self) -> &mut IntoIter<I::Item> {
21        if let Some(iter) = self.iter.take() {
22            self.buf = iter.collect::<Vec<_>>().into_iter();
23        }
24        &mut self.buf
25    }
26}
27
28macro_rules! choice {
29    ($self:ident:$name:ident $t:tt) => {
30        $self.iter.as_mut()
31            .map_or_else(|| $self.buf.$name $t, |iter| iter.$name $t)
32    };
33}
34
35impl<I: Iterator + FusedIterator> FusedIterator for ForwardIter<I> {}
36impl<I: Iterator + ExactSizeIterator> ExactSizeIterator for ForwardIter<I> {}
37impl<I: Iterator> Iterator for ForwardIter<I> {
38    type Item = I::Item;
39
40    fn next(&mut self) -> Option<Self::Item> {
41        choice!(self:next())
42    }
43
44    fn nth(&mut self, n: usize) -> Option<Self::Item> {
45        choice!(self:nth(n))
46    }
47
48    fn size_hint(&self) -> (usize, Option<usize>) {
49        self.iter.as_ref()
50            .map_or_else(|| self.buf.size_hint(), I::size_hint)
51    }
52
53    fn fold<B, F>(self, init: B, f: F) -> B
54    where Self: Sized,
55          F: FnMut(B, Self::Item) -> B,
56    {
57        match self.iter {
58            Some(iter) => iter.fold(init, f),
59            None => self.buf.fold(init, f),
60        }
61    }
62}
63impl<I: Iterator> DoubleEndedIterator for ForwardIter<I> {
64    fn next_back(&mut self) -> Option<Self::Item> {
65        self.backward().next_back()
66    }
67
68    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
69        self.backward().nth_back(n)
70    }
71
72    fn rfold<B, F>(mut self, init: B, f: F) -> B
73    where Self: Sized,
74          F: FnMut(B, Self::Item) -> B,
75    {
76        self.backward();
77        self.buf.rfold(init, f)
78    }
79}
80
81/// Ensure that side effects in iterators always run forward
82pub trait ForwardIterExt: Iterator + Sized {
83    /// Ensure that side effects in iterators always run forward
84    ///
85    /// Space complexity
86    /// - call [`Iterator`] methods: *O*(1)
87    /// - call [`DoubleEndedIterator`] methods: *O*(n)
88    ///
89    /// # Examples
90    ///
91    /// **Fail case**:
92    /// ```rust,should_panic
93    /// let mut i = 0;
94    /// let x: Vec<_> = (0..4).map(|_| { i += 1; i }).rev().collect();
95    /// assert_eq!(x, [4, 3, 2, 1]); // fail!
96    /// ```
97    ///
98    /// **Rewrite to**:
99    ///
100    /// ```rust
101    /// # use forward_iter::ForwardIterExt as _;
102    /// let mut i = 0;
103    /// let x: Vec<_> = (0..4).map(|_| { i += 1; i }).forward().rev().collect();
104    /// assert_eq!(x, [4, 3, 2, 1]); // success!
105    /// ```
106    fn forward(self) -> ForwardIter<Self> {
107        ForwardIter {
108            iter: Some(self),
109            buf: IntoIter::default(),
110        }
111    }
112}
113impl<I: Iterator> ForwardIterExt for I { }
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn some_next() {
121        let mut i = 0;
122        let iter = &mut (0..4).map(|_| { i += 1; i }).forward();
123        assert_eq!(iter.next(), Some(1));
124        let x: Vec<_> = iter.rev().collect();
125        assert_eq!(x, [4, 3, 2]);
126    }
127
128    #[test]
129    fn some_next2() {
130        let mut i = 0;
131        let iter = &mut (0..4).map(|_| { i += 1; i }).forward();
132        assert_eq!(iter.next(), Some(1));
133        assert_eq!(iter.next_back(), Some(4));
134        assert_eq!(iter.next(), Some(2));
135        assert_eq!(iter.next_back(), Some(3));
136        assert_eq!(iter.next(), None);
137        assert_eq!(iter.next_back(), None);
138    }
139}