Repository: rust-itertools/itertools
Pull request #910 (2024-04-03 01:24:46)

Implement DoubleEndedIterator for ProcessResults

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/rust-itertools/itertools/pull/910?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 94.43%. Comparing base [(`6814180`)](https://app.codecov.io/gh/rust-itertools/itertools/commit/68141809aabec2816d3250820c8d6608f3b2293f?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) to head [(`0227457`)](https://app.codecov.io/gh/rust-itertools/itertools/pull/910?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).
> Report is 42 commits behind head on master.


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #910      +/-   ##
==========================================
+ Coverage   94.38%   94.43%   +0.04%     
==========================================
  Files          48       48              
  Lines        6665     6829     +164     
==========================================
+ Hits         6291     6449     +158     
- Misses        374      380       +6     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/rust-itertools/itertools/pull/910?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).


Patch:
diff --git a/src/k_smallest.rs b/src/k_smallest.rs
index b909887..7b2f62e 100644
--- a/src/k_smallest.rs
+++ b/src/k_smallest.rs
@@ -42,8 +42,12 @@ where
     }
 
     if k == 0 {
+        iter.last();
         return Vec::new();
     }
+    if k == 1 {
+        return iter.min_by(comparator).into_iter().collect();
+    }
     let mut iter = iter.fuse();
     let mut storage: Vec<I::Item> = iter.by_ref().take(k).collect();
 
diff --git a/src/lib.rs b/src/lib.rs
index a297a6d..ff14cf9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2972,8 +2972,12 @@ pub trait Itertools: Iterator {
         use alloc::collections::BinaryHeap;
 
         if k == 0 {
+            self.last();
             return Vec::new().into_iter();
         }
+        if k == 1 {
+            return self.min().into_iter().collect_vec().into_iter();
+        }
 
         let mut iter = self.fuse();
         let mut heap: BinaryHeap<_> = iter.by_ref().take(k).collect();
diff --git a/src/process_results_impl.rs b/src/process_results_impl.rs
index 7b40cc0..ad6c60d 100644
--- a/src/process_results_impl.rs
+++ b/src/process_results_impl.rs
@@ -13,14 +13,10 @@ pub struct ProcessResults<'a, I, E: 'a> {
     iter: I,
 }
 
-impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E>
-where
-    I: Iterator<Item = Result<T, E>>,
-{
-    type Item = T;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        match self.iter.next() {
+impl<'a, I, E> ProcessResults<'a, I, E> {
+    #[inline(always)]
+    fn next_body<T>(&mut self, item: Option<Result<T, E>>) -> Option<T> {
+        match item {
             Some(Ok(x)) => Some(x),
             Some(Err(e)) => {
                 *self.error = Err(e);
@@ -29,6 +25,18 @@ where
             None => None,
         }
     }
+}
+
+impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E>
+where
+    I: Iterator<Item = Result<T, E>>,
+{
+    type Item = T;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let item = self.iter.next();
+        self.next_body(item)
+    }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
         (0, self.iter.size_hint().1)
@@ -52,6 +60,33 @@ where
     }
 }
 
+impl<'a, I, T, E> DoubleEndedIterator for ProcessResults<'a, I, E>
+where
+    I: Iterator<Item = Result<T, E>>,
+    I: DoubleEndedIterator,
+{
+    fn next_back(&mut self) -> Option<Self::Item> {
+        let item = self.iter.next_back();
+        self.next_body(item)
+    }
+
+    fn rfold<B, F>(mut self, init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let error = self.error;
+        self.iter
+            .try_rfold(init, |acc, opt| match opt {
+                Ok(x) => Ok(f(acc, x)),
+                Err(e) => {
+                    *error = Err(e);
+                    Err(acc)
+                }
+            })
+            .unwrap_or_else(|e| e)
+    }
+}
+
 /// “Lift” a function of the values of an iterator so that it can process
 /// an iterator of `Result` values instead.
 ///


Test patch:
diff --git a/tests/specializations.rs b/tests/specializations.rs
index fd8801e..949b2a7 100644
--- a/tests/specializations.rs
+++ b/tests/specializations.rs
@@ -466,7 +466,7 @@ quickcheck! {
         helper(v.iter().copied());
         helper(v.iter().copied().filter(Result::is_ok));
 
-        fn helper(it: impl Iterator<Item = Result<u8, u8>> + Clone) {
+        fn helper(it: impl Iterator<Item = Result<u8, u8>> + DoubleEndedIterator + Clone) {
             macro_rules! check_results_specialized {
                 ($src:expr, |$it:pat| $closure:expr) => {
                     assert_eq!(
@@ -482,6 +482,7 @@ quickcheck! {
             check_results_specialized!(it, |i| i.count());
             check_results_specialized!(it, |i| i.last());
             check_results_specialized!(it, |i| i.collect::<Vec<_>>());
+            check_results_specialized!(it, |i| i.rev().collect::<Vec<_>>());
             check_results_specialized!(it, |i| {
                 let mut parameters_from_fold = vec![];
                 let fold_result = i.fold(vec![], |mut acc, v| {
@@ -491,6 +492,15 @@ quickcheck! {
                 });
                 (parameters_from_fold, fold_result)
             });
+            check_results_specialized!(it, |i| {
+                let mut parameters_from_rfold = vec![];
+                let rfold_result = i.rfold(vec![], |mut acc, v| {
+                    parameters_from_rfold.push((acc.clone(), v));
+                    acc.push(v);
+                    acc
+                });
+                (parameters_from_rfold, rfold_result)
+            });
             check_results_specialized!(it, |mut i| {
                 let mut parameters_from_all = vec![];
                 let first = i.next();
@@ -504,6 +514,9 @@ quickcheck! {
             for n in 0..size + 2 {
                 check_results_specialized!(it, |mut i| i.nth(n));
             }
+            for n in 0..size + 2 {
+                check_results_specialized!(it, |mut i| i.nth_back(n));
+            }
         }
     }
 }