Skip to content

Commit 35eb3e8

Browse files
committed
Correct iterator adaptor Chain
The iterator protocol specifies that the iteration ends with the return value `None` from `.next()` (or `.next_back()`) and it is unspecified what further calls return. The chain adaptor must account for this in its DoubleEndedIterator implementation. It uses three states: - Both `a` and `b` are valid - Only the Front iterator (`a`) is valid - Only the Back iterator (`b`) is valid The fourth state (neither iterator is valid) only occurs after Chain has returned None once, so we don't need to store this state. Fixes #26316
1 parent 4c99649 commit 35eb3e8

File tree

2 files changed

+91
-25
lines changed

2 files changed

+91
-25
lines changed

src/libcore/iter.rs

Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ pub trait Iterator {
184184
fn chain<U>(self, other: U) -> Chain<Self, U::IntoIter> where
185185
Self: Sized, U: IntoIterator<Item=Self::Item>,
186186
{
187-
Chain{a: self, b: other.into_iter(), flag: false}
187+
Chain{a: self, b: other.into_iter(), state: ChainState::Both}
188188
}
189189

190190
/// Creates an iterator that iterates over both this and the specified
@@ -1277,7 +1277,30 @@ impl<I> Iterator for Cycle<I> where I: Clone + Iterator {
12771277
pub struct Chain<A, B> {
12781278
a: A,
12791279
b: B,
1280-
flag: bool,
1280+
state: ChainState,
1281+
}
1282+
1283+
// The iterator protocol specifies that iteration ends with the return value
1284+
// `None` from `.next()` (or `.next_back()`) and it is unspecified what
1285+
// further calls return. The chain adaptor must account for this since it uses
1286+
// two subiterators.
1287+
//
1288+
// It uses three states:
1289+
//
1290+
// - Both: `a` and `b` are remaining
1291+
// - Front: `a` remaining
1292+
// - Back: `b` remaining
1293+
//
1294+
// The fourth state (neither iterator is remaining) only occurs after Chain has
1295+
// returned None once, so we don't need to store this state.
1296+
#[derive(Clone)]
1297+
enum ChainState {
1298+
// both front and back iterator are remaining
1299+
Both,
1300+
// only front is remaining
1301+
Front,
1302+
// only back is remaining
1303+
Back,
12811304
}
12821305

12831306
#[stable(feature = "rust1", since = "1.0.0")]
@@ -1289,42 +1312,58 @@ impl<A, B> Iterator for Chain<A, B> where
12891312

12901313
#[inline]
12911314
fn next(&mut self) -> Option<A::Item> {
1292-
if self.flag {
1293-
self.b.next()
1294-
} else {
1295-
match self.a.next() {
1296-
Some(x) => return Some(x),
1297-
_ => ()
1298-
}
1299-
self.flag = true;
1300-
self.b.next()
1315+
match self.state {
1316+
ChainState::Both => match self.a.next() {
1317+
elt @ Some(..) => return elt,
1318+
None => {
1319+
self.state = ChainState::Back;
1320+
self.b.next()
1321+
}
1322+
},
1323+
ChainState::Front => self.a.next(),
1324+
ChainState::Back => self.b.next(),
13011325
}
13021326
}
13031327

13041328
#[inline]
13051329
fn count(self) -> usize {
1306-
(if !self.flag { self.a.count() } else { 0 }) + self.b.count()
1330+
match self.state {
1331+
ChainState::Both => self.a.count() + self.b.count(),
1332+
ChainState::Front => self.a.count(),
1333+
ChainState::Back => self.b.count(),
1334+
}
13071335
}
13081336

13091337
#[inline]
13101338
fn nth(&mut self, mut n: usize) -> Option<A::Item> {
1311-
if !self.flag {
1312-
for x in self.a.by_ref() {
1313-
if n == 0 {
1314-
return Some(x)
1339+
match self.state {
1340+
ChainState::Both | ChainState::Front => {
1341+
for x in self.a.by_ref() {
1342+
if n == 0 {
1343+
return Some(x)
1344+
}
1345+
n -= 1;
1346+
}
1347+
if let ChainState::Both = self.state {
1348+
self.state = ChainState::Back;
13151349
}
1316-
n -= 1;
13171350
}
1318-
self.flag = true;
1351+
ChainState::Back => {}
1352+
}
1353+
if let ChainState::Back = self.state {
1354+
self.b.nth(n)
1355+
} else {
1356+
None
13191357
}
1320-
self.b.nth(n)
13211358
}
13221359

13231360
#[inline]
13241361
fn last(self) -> Option<A::Item> {
1325-
let a_last = if self.flag { None } else { self.a.last() };
1326-
let b_last = self.b.last();
1327-
b_last.or(a_last)
1362+
match self.state {
1363+
ChainState::Both => self.b.last().or(self.a.last()),
1364+
ChainState::Front => self.a.last(),
1365+
ChainState::Back => self.b.last()
1366+
}
13281367
}
13291368

13301369
#[inline]
@@ -1350,9 +1389,16 @@ impl<A, B> DoubleEndedIterator for Chain<A, B> where
13501389
{
13511390
#[inline]
13521391
fn next_back(&mut self) -> Option<A::Item> {
1353-
match self.b.next_back() {
1354-
Some(x) => Some(x),
1355-
None => self.a.next_back()
1392+
match self.state {
1393+
ChainState::Both => match self.b.next_back() {
1394+
elt @ Some(..) => return elt,
1395+
None => {
1396+
self.state = ChainState::Front;
1397+
self.a.next_back()
1398+
}
1399+
},
1400+
ChainState::Front => self.a.next_back(),
1401+
ChainState::Back => self.b.next_back(),
13561402
}
13571403
}
13581404
}

src/libcoretest/iter.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,26 @@ fn test_double_ended_chain() {
729729
assert_eq!(it.next_back().unwrap(), &5);
730730
assert_eq!(it.next_back().unwrap(), &7);
731731
assert_eq!(it.next_back(), None);
732+
733+
734+
// test that .chain() is well behaved with an unfused iterator
735+
struct CrazyIterator(bool);
736+
impl CrazyIterator { fn new() -> CrazyIterator { CrazyIterator(false) } }
737+
impl Iterator for CrazyIterator {
738+
type Item = i32;
739+
fn next(&mut self) -> Option<i32> {
740+
if self.0 { Some(99) } else { self.0 = true; None }
741+
}
742+
}
743+
744+
impl DoubleEndedIterator for CrazyIterator {
745+
fn next_back(&mut self) -> Option<i32> {
746+
self.next()
747+
}
748+
}
749+
750+
assert_eq!(CrazyIterator::new().chain(0..10).rev().last(), Some(0));
751+
assert!((0..10).chain(CrazyIterator::new()).rev().any(|i| i == 0));
732752
}
733753

734754
#[test]

0 commit comments

Comments
 (0)