Skip to content

Commit ff352cd

Browse files
authored
Rollup merge of #63691 - timvermeulen:chain-size-hint, r=scottmcm
Fix bug in iter::Chain::size_hint `Chain::size_hint` currently ignores `self.state`, which means that the size hints of the underlying iterators are always combined regardless of the iteration state. This, of course, should only happen when the state is `ChainState::Both`.
2 parents 60649e3 + ec54340 commit ff352cd

File tree

2 files changed

+62
-8
lines changed

2 files changed

+62
-8
lines changed

src/libcore/iter/adapters/chain.rs

+14-8
Original file line numberDiff line numberDiff line change
@@ -173,17 +173,23 @@ impl<A, B> Iterator for Chain<A, B> where
173173

174174
#[inline]
175175
fn size_hint(&self) -> (usize, Option<usize>) {
176-
let (a_lower, a_upper) = self.a.size_hint();
177-
let (b_lower, b_upper) = self.b.size_hint();
176+
match self.state {
177+
ChainState::Both => {
178+
let (a_lower, a_upper) = self.a.size_hint();
179+
let (b_lower, b_upper) = self.b.size_hint();
178180

179-
let lower = a_lower.saturating_add(b_lower);
181+
let lower = a_lower.saturating_add(b_lower);
180182

181-
let upper = match (a_upper, b_upper) {
182-
(Some(x), Some(y)) => x.checked_add(y),
183-
_ => None
184-
};
183+
let upper = match (a_upper, b_upper) {
184+
(Some(x), Some(y)) => x.checked_add(y),
185+
_ => None
186+
};
185187

186-
(lower, upper)
188+
(lower, upper)
189+
}
190+
ChainState::Front => self.a.size_hint(),
191+
ChainState::Back => self.b.size_hint(),
192+
}
187193
}
188194
}
189195

src/libcore/tests/iter.rs

+48
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,54 @@ fn test_iterator_chain_find() {
152152
assert_eq!(iter.next(), None);
153153
}
154154

155+
#[test]
156+
fn test_iterator_chain_size_hint() {
157+
struct Iter {
158+
is_empty: bool,
159+
}
160+
161+
impl Iterator for Iter {
162+
type Item = ();
163+
164+
// alternates between `None` and `Some(())`
165+
fn next(&mut self) -> Option<Self::Item> {
166+
if self.is_empty {
167+
self.is_empty = false;
168+
None
169+
} else {
170+
self.is_empty = true;
171+
Some(())
172+
}
173+
}
174+
175+
fn size_hint(&self) -> (usize, Option<usize>) {
176+
if self.is_empty {
177+
(0, Some(0))
178+
} else {
179+
(1, Some(1))
180+
}
181+
}
182+
}
183+
184+
impl DoubleEndedIterator for Iter {
185+
fn next_back(&mut self) -> Option<Self::Item> {
186+
self.next()
187+
}
188+
}
189+
190+
// this chains an iterator of length 0 with an iterator of length 1,
191+
// so after calling `.next()` once, the iterator is empty and the
192+
// state is `ChainState::Back`. `.size_hint()` should now disregard
193+
// the size hint of the left iterator
194+
let mut iter = Iter { is_empty: true }.chain(once(()));
195+
assert_eq!(iter.next(), Some(()));
196+
assert_eq!(iter.size_hint(), (0, Some(0)));
197+
198+
let mut iter = once(()).chain(Iter { is_empty: true });
199+
assert_eq!(iter.next_back(), Some(()));
200+
assert_eq!(iter.size_hint(), (0, Some(0)));
201+
}
202+
155203
#[test]
156204
fn test_zip_nth() {
157205
let xs = [0, 1, 2, 4, 5];

0 commit comments

Comments
 (0)