about summary refs log tree commit diff
path: root/src/libcore/iter
diff options
context:
space:
mode:
authorJosh Stone <jistone@redhat.com>2020-04-21 14:56:59 -0700
committerJosh Stone <jistone@redhat.com>2020-04-21 14:56:59 -0700
commiteeb687f20e86f2e2cf61ef89139c102cb92abfcb (patch)
treee2a60e667c3e5f0633089e719552f5628f8c1f45 /src/libcore/iter
parent45d050cde277b22a755847338f2acc2c7b834141 (diff)
downloadrust-eeb687f20e86f2e2cf61ef89139c102cb92abfcb.tar.gz
rust-eeb687f20e86f2e2cf61ef89139c102cb92abfcb.zip
Don't fuse Chain in its second iterator
Only the "first" iterator is actually set `None` when exhausted,
depending on whether you iterate forward or backward. This restores
behavior similar to the former `ChainState`, where it would transition
from `Both` to `Front`/`Back` and only continue from that side.

However, if you mix directions, then this may still set both sides to
`None`, totally fusing the iterator.
Diffstat (limited to 'src/libcore/iter')
-rw-r--r--src/libcore/iter/adapters/chain.rs32
1 files changed, 22 insertions, 10 deletions
diff --git a/src/libcore/iter/adapters/chain.rs b/src/libcore/iter/adapters/chain.rs
index 2dd405ced20..6700ef017bd 100644
--- a/src/libcore/iter/adapters/chain.rs
+++ b/src/libcore/iter/adapters/chain.rs
@@ -18,6 +18,9 @@ pub struct Chain<A, B> {
     // adapter because its specialization for `FusedIterator` unconditionally descends into the
     // iterator, and that could be expensive to keep revisiting stuff like nested chains. It also
     // hurts compiler performance to add more iterator layers to `Chain`.
+    //
+    // Only the "first" iterator is actually set `None` when exhausted, depending on whether you
+    // iterate forward or backward. If you mix directions, then both sides may be `None`.
     a: Option<A>,
     b: Option<B>,
 }
@@ -43,6 +46,17 @@ macro_rules! fuse {
     };
 }
 
+/// Try an iterator method without fusing,
+/// like an inline `.as_mut().and_then(...)`
+macro_rules! maybe {
+    ($self:ident . $iter:ident . $($call:tt)+) => {
+        match $self.$iter {
+            Some(ref mut iter) => iter.$($call)+,
+            None => None,
+        }
+    };
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<A, B> Iterator for Chain<A, B>
 where
@@ -54,7 +68,7 @@ where
     #[inline]
     fn next(&mut self) -> Option<A::Item> {
         match fuse!(self.a.next()) {
-            None => fuse!(self.b.next()),
+            None => maybe!(self.b.next()),
             item => item,
         }
     }
@@ -85,7 +99,7 @@ where
         }
         if let Some(ref mut b) = self.b {
             acc = b.try_fold(acc, f)?;
-            self.b = None;
+            // we don't fuse the second iterator
         }
         Try::from_ok(acc)
     }
@@ -114,7 +128,7 @@ where
             }
             self.a = None;
         }
-        fuse!(self.b.nth(n))
+        maybe!(self.b.nth(n))
     }
 
     #[inline]
@@ -123,7 +137,7 @@ where
         P: FnMut(&Self::Item) -> bool,
     {
         match fuse!(self.a.find(&mut predicate)) {
-            None => fuse!(self.b.find(predicate)),
+            None => maybe!(self.b.find(predicate)),
             item => item,
         }
     }
@@ -174,7 +188,7 @@ where
     #[inline]
     fn next_back(&mut self) -> Option<A::Item> {
         match fuse!(self.b.next_back()) {
-            None => fuse!(self.a.next_back()),
+            None => maybe!(self.a.next_back()),
             item => item,
         }
     }
@@ -190,7 +204,7 @@ where
             }
             self.b = None;
         }
-        fuse!(self.a.nth_back(n))
+        maybe!(self.a.nth_back(n))
     }
 
     #[inline]
@@ -199,7 +213,7 @@ where
         P: FnMut(&Self::Item) -> bool,
     {
         match fuse!(self.b.rfind(&mut predicate)) {
-            None => fuse!(self.a.rfind(predicate)),
+            None => maybe!(self.a.rfind(predicate)),
             item => item,
         }
     }
@@ -216,7 +230,7 @@ where
         }
         if let Some(ref mut a) = self.a {
             acc = a.try_rfold(acc, f)?;
-            self.a = None;
+            // we don't fuse the second iterator
         }
         Try::from_ok(acc)
     }
@@ -236,8 +250,6 @@ where
 }
 
 // Note: *both* must be fused to handle double-ended iterators.
-// Now that we "fuse" both sides, we *could* implement this unconditionally,
-// but we should be cautious about committing to that in the public API.
 #[stable(feature = "fused", since = "1.26.0")]
 impl<A, B> FusedIterator for Chain<A, B>
 where