double_ended_peekable/
lib.rs

1//! A peekable abstraction for double-ended iterators.
2//!
3//! This crate provides an _extension trait_ to standard [`Iterator`] in order to lift the
4//! [`Peekable`] abstraction over types implementing [`DoubleEndedIterator`].
5//!
6//! # Basic usage
7//!
8//! ```
9//! use double_ended_peekable::DoubleEndedPeekableExt;
10//!
11//! // Now you can use `iter.double_ended_peekable()`
12//! let mut iter = [1, 2, 3, 4].into_iter().double_ended_peekable();
13//! // Same abstractions of `Peekable`
14//! assert_eq!(iter.peek(), Some(&1));
15//! // Additional `*_back*` methods
16//! assert_eq!(iter.peek_back(), Some(&4));
17//! // It implements `Iterator`
18//! assert_eq!(iter.next(), Some(1));
19//! // And also `DoubleEndedIterator` when possible
20//! assert_eq!(iter.next_back(), Some(4));
21//! // Additional methods for both front and back items
22//! assert_eq!(iter.next_front_back_if_eq(&2, &3), Some((2, 3)));
23//! ```
24//!
25//! Check [`DoubleEndedPeekable`] documentation for additional information.
26//!
27//! # Rationale
28//!
29//! It is possible to use [`Peekable`] on double-ended iterators using `.rev().peekable()`:
30//!
31//! ```
32//! let mut iter = [1, 2, 3].into_iter().rev().peekable();
33//! // No problem!
34//! assert_eq!(iter.peek(), Some(&3));
35//! ````
36//!
37//! However, using `.by_ref().rev().peekable()` _on the fly_ is a footgun:
38//! ```should_panic
39//! let mut iter = [1, 2, 3, 4].into_iter().peekable();
40//! assert_eq!(iter.peek(), Some(&1));
41//! assert_eq!(iter.by_ref().rev().peekable().peek(), Some(&4));
42//! assert_eq!(iter.next(), Some(1));
43//!
44//! // Dang! This fails: iter.next_back() == Some(3)
45//! assert_eq!(iter.next_back(), Some(4));
46//! ```
47//!
48//! The assertion fails just because [`Peekable`] saves the next item of the iterator internally.
49//! Therefore, creating a _rev-peekable_ iterator on the fly is risky because there is a good
50//! chance a peeked element is going to be accidentally lost.
51//!
52//! This tiny crate exposes a simple but powerful abstraction that is hard to misuse.
53//!
54//! [`Peekable`]: core::iter::Peekable
55
56#![cfg_attr(not(test), no_std)]
57
58#[cfg(test)]
59mod tests;
60
61use core::{
62    fmt::{self, Debug},
63    hash::{Hash, Hasher},
64    hint::unreachable_unchecked,
65    mem,
66};
67
68/// An _extension trait_ to create [`DoubleEndedPeekable`].
69///
70/// This has a blanket implementation for all types that implement [`Iterator`].
71pub trait DoubleEndedPeekableExt<I: Iterator> {
72    /// Creates an iterator which works similarly to [`Peekable`], but also provides additional
73    /// functions if the underlying type implements [`DoubleEndedIterator`].
74    ///
75    /// See [`DoubleEndedPeekable`] for more information.
76    ///
77    /// [`Peekable`]: core::iter::Peekable
78    fn double_ended_peekable(self) -> DoubleEndedPeekable<I>;
79}
80
81impl<I> DoubleEndedPeekableExt<I> for I
82where
83    I: Iterator,
84{
85    #[inline]
86    fn double_ended_peekable(self) -> DoubleEndedPeekable<I> {
87        DoubleEndedPeekable {
88            iter: self,
89            front: MaybePeeked::Unpeeked,
90            back: MaybePeeked::Unpeeked,
91        }
92    }
93}
94
95/// An advanced version of [`Peekable`] that works well with double-ended iterators.
96///
97/// This `struct` is created by the [`double_ended_peekable`] method on [`DoubleEndedPeekableExt`].
98///
99/// [`Peekable`]: core::iter::Peekable
100/// [`double_ended_peekable`]: DoubleEndedPeekableExt::double_ended_peekable
101pub struct DoubleEndedPeekable<I: Iterator> {
102    iter: I,
103    front: MaybePeeked<<I as Iterator>::Item>,
104    back: MaybePeeked<<I as Iterator>::Item>,
105}
106
107impl<I: Iterator> DoubleEndedPeekable<I> {
108    /// Returns a reference to the `next()` value without advancing the iterator.
109    ///
110    /// See [`Peekable::peek`] for more information.
111    ///
112    /// [`Peekable::peek`]: core::iter::Peekable::peek
113    #[inline]
114    pub fn peek(&mut self) -> Option<&I::Item> {
115        self.front
116            .get_peeked_or_insert_with(|| self.iter.next())
117            .as_ref()
118            .or_else(|| self.back.peeked_value_ref())
119    }
120
121    /// Returns a mutable reference to the `next()` value without advancing the iterator.
122    ///
123    /// See [`Peekable::peek_mut`] for more information.
124    ///
125    /// [`Peekable::peek_mut`]: core::iter::Peekable::peek_mut
126    #[inline]
127    pub fn peek_mut(&mut self) -> Option<&mut I::Item> {
128        self.front
129            .get_peeked_or_insert_with(|| self.iter.next())
130            .as_mut()
131            .or_else(|| self.back.peeked_value_mut())
132    }
133
134    /// Consumes and returns the next value of this iterator if a condition is true.
135    ///
136    /// See [`Peekable::next_if`] for more information.
137    ///
138    /// [`Peekable::next_if`]: core::iter::Peekable::next_if
139    #[inline]
140    pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option<I::Item> {
141        match self.next() {
142            Some(item) if func(&item) => Some(item),
143            other => {
144                debug_assert!(self.front.is_unpeeked());
145                self.front = MaybePeeked::Peeked(other);
146                None
147            }
148        }
149    }
150
151    /// Consumes and returns the next item if it is equal to `expected`.
152    ///
153    /// See [`Peekable::next_if_eq`] for more information.
154    ///
155    /// [`Peekable::next_if_eq`]: core::iter::Peekable::next_if
156    #[inline]
157    pub fn next_if_eq<T>(&mut self, expected: &T) -> Option<I::Item>
158    where
159        T: ?Sized,
160        I::Item: PartialEq<T>,
161    {
162        self.next_if(|item| item == expected)
163    }
164}
165
166impl<I: DoubleEndedIterator> DoubleEndedPeekable<I> {
167    /// Returns a reference to the `next_back()` value without advancing the _back_ of the iterator.
168    ///
169    /// Like [`next_back`], if there is a value, it is wrapped in a `Some(T)`.
170    /// But if the iteration is over, `None` is returned.
171    ///
172    /// [`next_back`]: DoubleEndedIterator::next_back
173    ///
174    /// Because `peek_back()` returns a reference, and many iterators iterate over references,
175    /// there can be a possibly confusing situation where the return value is a double reference.
176    /// You can see this effect in the examples below.
177    ///
178    /// # Examples
179    ///
180    /// Basic usage:
181    ///
182    /// ```
183    /// use double_ended_peekable::DoubleEndedPeekableExt;
184    ///
185    /// let xs = [1, 2, 3];
186    ///
187    /// let mut iter = xs.into_iter().double_ended_peekable();
188    ///
189    /// // peek_back() lets us see into the past of the future
190    /// assert_eq!(iter.peek_back(), Some(&3));
191    /// assert_eq!(iter.next_back(), Some(3));
192    ///
193    /// assert_eq!(iter.next_back(), Some(2));
194    ///
195    /// // The iterator does not advance even if we `peek_back` multiple times
196    /// assert_eq!(iter.peek_back(), Some(&1));
197    /// assert_eq!(iter.peek_back(), Some(&1));
198    ///
199    /// assert_eq!(iter.next_back(), Some(1));
200    ///
201    /// // After the iterator is finished, so is `peek_back()`
202    /// assert_eq!(iter.peek_back(), None);
203    /// assert_eq!(iter.next_back(), None);
204    /// ```
205    #[inline]
206    pub fn peek_back(&mut self) -> Option<&I::Item> {
207        self.back
208            .get_peeked_or_insert_with(|| self.iter.next_back())
209            .as_ref()
210            .or_else(|| self.front.peeked_value_ref())
211    }
212
213    /// Returns a mutable reference to the `next_back()` value without advancing the _back_ of the
214    /// iterator.
215    ///
216    /// Like [`next_back`], if there is a value, it is wrapped in a `Some(T)`. But if the iteration
217    /// is over, `None` is returned.
218    ///
219    /// Because `peek_back_mut()` returns a reference, and many iterators iterate over references,
220    /// there can be a possibly confusing situation where the return value is a double reference.
221    /// You can see this effect in the examples below.
222    ///
223    /// [`next_back`]: DoubleEndedIterator::next_back
224    ///
225    /// # Examples
226    ///
227    /// Basic usage:
228    ///
229    /// ```
230    /// use double_ended_peekable::DoubleEndedPeekableExt;
231    ///
232    /// let mut iter = [1, 2, 3].into_iter().double_ended_peekable();
233    ///
234    /// // Like with `peek_back()`, we can see into the past of the future without advancing the
235    /// // iterator.
236    /// assert_eq!(iter.peek_back_mut(), Some(&mut 3));
237    /// assert_eq!(iter.peek_back_mut(), Some(&mut 3));
238    /// assert_eq!(iter.next_back(), Some(3));
239    ///
240    /// // Peek into the _back_ of the iterator and set the value behind the mutable reference.
241    /// if let Some(p) = iter.peek_back_mut() {
242    ///     assert_eq!(*p, 2);
243    ///     *p = 5;
244    /// }
245    ///
246    /// // The value we put in reappears as the iterator continues.
247    /// assert_eq!(iter.collect::<Vec<_>>(), vec![1, 5]);
248    /// ```
249    #[inline]
250    pub fn peek_back_mut(&mut self) -> Option<&mut I::Item> {
251        self.back
252            .get_peeked_or_insert_with(|| self.iter.next_back())
253            .as_mut()
254            .or_else(|| self.front.peeked_value_mut())
255    }
256
257    /// Consumes and returns the _next back_ value of this iterator if a condition is true.
258    ///
259    /// If `func` returns `true` for the _next back_ value of this iterator, it consumes the
260    /// element and returns it. Otherwise, it returns `None`.
261    ///
262    /// # Examples
263    /// Consume a number if it's equal to 4.
264    /// ```
265    /// use double_ended_peekable::DoubleEndedPeekableExt;
266    ///
267    /// let mut iter = (0..5).double_ended_peekable();
268    /// // The last item of the iterator is 4; consume it.
269    /// assert_eq!(iter.next_back_if(|&x| x == 4), Some(4));
270    /// // The _next back_ item returned is now 3, so `consume` will return `false`.
271    /// assert_eq!(iter.next_back_if(|&x| x == 4), None);
272    /// // `next_back_if` saves the value of the _next back_ item if it was not equal to
273    /// // `expected`.
274    /// assert_eq!(iter.next_back(), Some(3));
275    /// ```
276    ///
277    /// Consume any number greater than 10.
278    /// ```
279    /// use double_ended_peekable::DoubleEndedPeekableExt;
280    ///
281    /// let mut iter = (1..20).double_ended_peekable();
282    /// // Consume all numbers greater than 10
283    /// while iter.next_back_if(|&x| x > 10).is_some() {}
284    /// // The _next _back_ value returned will be 10
285    /// assert_eq!(iter.next_back(), Some(10));
286    /// ```
287    #[inline]
288    pub fn next_back_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option<I::Item> {
289        match self.next_back() {
290            Some(item) if func(&item) => Some(item),
291            other => {
292                debug_assert!(self.back.is_unpeeked());
293                self.back = MaybePeeked::Peeked(other);
294                None
295            }
296        }
297    }
298
299    /// Consumes and returns the _next back_ item if it is equal to `expected`.
300    ///
301    /// # Example
302    /// Consume a number if it's equal to 4.
303    /// ```
304    /// use double_ended_peekable::DoubleEndedPeekableExt;
305    ///
306    /// let mut iter = (0..5).double_ended_peekable();
307    /// // The first item of the iterator is 4; consume it.
308    /// assert_eq!(iter.next_back_if_eq(&4), Some(4));
309    /// // The next item returned is now 3, so `consume` will return `false`.
310    /// assert_eq!(iter.next_back_if_eq(&4), None);
311    /// // `next_if_eq` saves the value of the _next back_ item if it was not equal to `expected`.
312    /// assert_eq!(iter.next_back(), Some(3));
313    /// ```
314    #[inline]
315    pub fn next_back_if_eq<T>(&mut self, expected: &T) -> Option<I::Item>
316    where
317        T: ?Sized,
318        I::Item: PartialEq<T>,
319    {
320        self.next_back_if(|item| item == expected)
321    }
322
323    /// Consumes and returns the _front_ and _back_ elements of this iterator if a condition is true.
324    ///
325    /// If `func` returns `true` given the references to the _front_ and _back_ elements of this
326    /// iterator, it consumes the elements and returns them. Otherwise, it returns `None`.
327    ///
328    /// If there is only one element left, it returns `None`;
329    ///
330    /// # Examples
331    /// Consume a pair of numbers if the first is 0 and the second is 4.
332    /// ```
333    /// use double_ended_peekable::DoubleEndedPeekableExt;
334    ///
335    /// let mut iter = (0..5).double_ended_peekable();
336    /// // The first item of the iterator is 0 and the last is 4; consume it.
337    /// assert_eq!(iter.next_front_back_if(|&a, &b| a == 0 && b == 4), Some((0, 4)));
338    /// // The pair returned is now `(1, 3)`, so `consume` will return `false`.
339    /// assert_eq!(iter.next_front_back_if(|&a, &b| a == 0 && b == 4), None);
340    /// // `next_front_back_if` saves the both the _front_ and the _back_ values if the function
341    /// // returned `false`.
342    /// assert_eq!(iter.next(), Some(1));
343    /// assert_eq!(iter.next_back(), Some(3));
344    /// ```
345    ///
346    /// Consume any number greater than 10, in pairs.
347    /// ```
348    /// use double_ended_peekable::DoubleEndedPeekableExt;
349    ///
350    /// let mut iter = [12, 11, 10, 9, 10, 11, 12, 13].into_iter().double_ended_peekable();
351    /// // Consume all numbers greater than 10, in pairs
352    /// while iter.next_front_back_if(|&a, &b| a > 10 && b > 10).is_some() {}
353    /// // The remaining elements
354    /// assert_eq!(iter.collect::<Vec<_>>(), [10, 9, 10, 11]);
355    /// ```
356    #[inline]
357    pub fn next_front_back_if(
358        &mut self,
359        func: impl FnOnce(&I::Item, &I::Item) -> bool,
360    ) -> Option<(I::Item, I::Item)> {
361        match (self.next(), self.next_back()) {
362            (Some(front), Some(back)) if func(&front, &back) => Some((front, back)),
363            (front, back) => {
364                debug_assert!(self.front.is_unpeeked());
365                debug_assert!(self.back.is_unpeeked());
366                self.front = MaybePeeked::Peeked(front);
367                self.back = MaybePeeked::Peeked(back);
368                None
369            }
370        }
371    }
372
373    /// Consumes and returns the _front_ and _back_ elements of this iterator if they are equal to
374    /// the expected values.
375    ///
376    /// # Example
377    /// Consume any number if they are 10 and 15, respectively.
378    /// ```
379    /// use double_ended_peekable::DoubleEndedPeekableExt;
380    ///
381    /// let mut iter = [10, 10, 9, 15].into_iter().double_ended_peekable();
382    /// // The first and the last items of the iterator are 10 and 15; consume it.
383    /// while iter.next_front_back_if_eq(&10, &15).is_some() {}
384    /// // The remaining elements
385    /// assert_eq!(iter.collect::<Vec<_>>(), [10, 9]);
386    /// ```
387    #[inline]
388    pub fn next_front_back_if_eq<T>(
389        &mut self,
390        expected_front: &T,
391        expected_back: &T,
392    ) -> Option<(I::Item, I::Item)>
393    where
394        T: ?Sized,
395        I::Item: PartialEq<T>,
396    {
397        self.next_front_back_if(|front, back| front == expected_front && back == expected_back)
398    }
399}
400
401impl<I> Iterator for DoubleEndedPeekable<I>
402where
403    I: Iterator,
404{
405    type Item = I::Item;
406
407    #[inline]
408    fn next(&mut self) -> Option<Self::Item> {
409        match self.front.take() {
410            MaybePeeked::Peeked(out @ Some(_)) => out,
411            MaybePeeked::Peeked(None) => self.back.take().into_peeked_value(),
412            MaybePeeked::Unpeeked => match self.iter.next() {
413                item @ Some(_) => item,
414                None => self.back.take().into_peeked_value(),
415            },
416        }
417    }
418
419    #[inline]
420    fn size_hint(&self) -> (usize, Option<usize>) {
421        let (lower, upper) = self.iter.size_hint();
422        let additional = match (&self.front, &self.back) {
423            (MaybePeeked::Peeked(_), MaybePeeked::Peeked(_)) => 2,
424            (MaybePeeked::Peeked(_), _) | (_, MaybePeeked::Peeked(_)) => 1,
425            (MaybePeeked::Unpeeked, MaybePeeked::Unpeeked) => 0,
426        };
427
428        (lower + additional, upper.map(|upper| upper + additional))
429    }
430}
431
432impl<I> DoubleEndedIterator for DoubleEndedPeekable<I>
433where
434    I: DoubleEndedIterator,
435{
436    #[inline]
437    fn next_back(&mut self) -> Option<Self::Item> {
438        match self.back.take() {
439            MaybePeeked::Peeked(out @ Some(_)) => out,
440            MaybePeeked::Peeked(None) => self.front.take().into_peeked_value(),
441            MaybePeeked::Unpeeked => match self.iter.next_back() {
442                out @ Some(_) => out,
443                None => self.front.take().into_peeked_value(),
444            },
445        }
446    }
447}
448
449impl<I> Debug for DoubleEndedPeekable<I>
450where
451    I: Iterator + Debug,
452    I::Item: Debug,
453{
454    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
455        f.debug_struct("DoubleEndedPeekable")
456            .field("iter", &self.iter)
457            .field("front", &self.front)
458            .field("back", &self.back)
459            .finish()
460    }
461}
462
463impl<I> Clone for DoubleEndedPeekable<I>
464where
465    I: Iterator + Clone,
466    I::Item: Clone,
467{
468    #[inline]
469    fn clone(&self) -> Self {
470        Self {
471            iter: self.iter.clone(),
472            front: self.front.clone(),
473            back: self.back.clone(),
474        }
475    }
476}
477
478impl<I> PartialEq for DoubleEndedPeekable<I>
479where
480    I: Iterator + PartialEq,
481    I::Item: PartialEq,
482{
483    #[inline]
484    fn eq(&self, other: &Self) -> bool {
485        self.iter == other.iter && self.front == other.front && self.back == other.back
486    }
487}
488
489impl<I> Eq for DoubleEndedPeekable<I>
490where
491    I: Iterator + Eq,
492    I::Item: Eq,
493{
494}
495
496impl<I> Hash for DoubleEndedPeekable<I>
497where
498    I: Iterator + Hash,
499    I::Item: Hash,
500{
501    #[inline]
502    fn hash<H: Hasher>(&self, state: &mut H) {
503        self.iter.hash(state);
504        self.front.hash(state);
505        self.back.hash(state);
506    }
507}
508
509#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
510enum MaybePeeked<T> {
511    #[default]
512    Unpeeked,
513    Peeked(Option<T>),
514}
515
516impl<T> MaybePeeked<T> {
517    fn get_peeked_or_insert_with<F>(&mut self, f: F) -> &mut Option<T>
518    where
519        F: FnOnce() -> Option<T>,
520    {
521        if let MaybePeeked::Unpeeked = self {
522            *self = MaybePeeked::Peeked(f());
523        }
524
525        let MaybePeeked::Peeked(peeked) = self else {
526            // SAFETY: it cannot be `Unpeeked` because that case has been just replaced with
527            // `Peeked`, and we only have two possible states.
528            unsafe { unreachable_unchecked() }
529        };
530        peeked
531    }
532
533    const fn peeked_value_ref(&self) -> Option<&T> {
534        match self {
535            MaybePeeked::Unpeeked | MaybePeeked::Peeked(None) => None,
536            MaybePeeked::Peeked(Some(peeked)) => Some(peeked),
537        }
538    }
539
540    fn peeked_value_mut(&mut self) -> Option<&mut T> {
541        match self {
542            MaybePeeked::Unpeeked | MaybePeeked::Peeked(None) => None,
543            MaybePeeked::Peeked(Some(peeked)) => Some(peeked),
544        }
545    }
546
547    const fn is_unpeeked(&self) -> bool {
548        matches!(self, MaybePeeked::Unpeeked)
549    }
550
551    fn take(&mut self) -> Self {
552        mem::replace(self, MaybePeeked::Unpeeked)
553    }
554
555    fn into_peeked_value(self) -> Option<T> {
556        match self {
557            MaybePeeked::Unpeeked | MaybePeeked::Peeked(None) => None,
558            MaybePeeked::Peeked(Some(peeked)) => Some(peeked),
559        }
560    }
561}