diff --git a/doc/tutorial-container.md b/doc/tutorial-container.md index f2c3d8d4450f2..7851e9852c115 100644 --- a/doc/tutorial-container.md +++ b/doc/tutorial-container.md @@ -105,6 +105,10 @@ impl Iterator for ZeroStream { } ~~~ +In general, you cannot rely on the behavior of the `next()` method after it has +returned `None`. Some iterators may return `None` forever. Others may behave +differently. + ## Container iterators Containers implement iteration over the contained elements by returning an @@ -112,7 +116,7 @@ iterator object. For example, vector slices several iterators available: * `iter()` and `rev_iter()`, for immutable references to the elements * `mut_iter()` and `mut_rev_iter()`, for mutable references to the elements -* `move_iter()` and `move_rev_iter`, to move the elements out by-value +* `move_iter()` and `move_rev_iter()`, to move the elements out by-value A typical mutable container will implement at least `iter()`, `mut_iter()` and `move_iter()` along with the reverse variants if it maintains an order. @@ -149,7 +153,7 @@ let result = xs.iter().fold(0, |accumulator, item| accumulator - *item); assert_eq!(result, -41); ~~~ -Some adaptors return an adaptor object implementing the `Iterator` trait itself: +Most adaptors return an adaptor object implementing the `Iterator` trait itself: ~~~ let xs = [1, 9, 2, 3, 14, 12]; @@ -158,6 +162,35 @@ let sum = xs.iter().chain(ys.iter()).fold(0, |a, b| a + *b); assert_eq!(sum, 57); ~~~ +Some iterator adaptors may return `None` before exhausting the underlying +iterator. Additionally, if these iterator adaptors are called again after +returning `None`, they may call their underlying iterator again even if the +adaptor will continue to return `None` forever. This may not be desired if the +underlying iterator has side-effects. + +In order to provide a guarantee about behavior once `None` has been returned, an +iterator adaptor named `fuse()` is provided. This returns an iterator that will +never call its underlying iterator again once `None` has been returned: + +~~~ +let xs = [1,2,3,4,5]; +let mut calls = 0; +let it = xs.iter().scan((), |_, x| { + calls += 1; + if *x < 3 { Some(x) } else { None }}); +// the iterator will only yield 1 and 2 before returning None +// If we were to call it 5 times, calls would end up as 5, despite only 2 values +// being yielded (and therefore 3 unique calls being made). The fuse() adaptor +// can fix this. +let mut it = it.fuse(); +it.next(); +it.next(); +it.next(); +it.next(); +it.next(); +assert_eq!(calls, 3); +~~~ + ## For loops The function `range` (or `range_inclusive`) allows to simply iterate through a given range: diff --git a/src/libstd/iterator.rs b/src/libstd/iterator.rs index 4af7b3e242596..3b4c31349c9c2 100644 --- a/src/libstd/iterator.rs +++ b/src/libstd/iterator.rs @@ -41,6 +41,13 @@ pub trait Extendable: FromIterator { /// An interface for dealing with "external iterators". These types of iterators /// can be resumed at any time as all state is stored internally as opposed to /// being located on the call stack. +/// +/// The Iterator protocol states that an iterator yields a (potentially-empty, +/// potentially-infinite) sequence of values, and returns `None` to signal that +/// it's finished. The Iterator protocol does not define behavior after `None` +/// is returned. A concrete Iterator implementation may choose to behave however +/// it wishes, either by returning `None` infinitely, or by doing something +/// else. pub trait Iterator { /// Advance the iterator and return the next value. Return `None` when the end is reached. fn next(&mut self) -> Option; @@ -300,6 +307,36 @@ pub trait Iterator { FlatMap{iter: self, f: f, frontiter: None, backiter: None } } + /// Creates an iterator that yields `None` forever after the underlying + /// iterator yields `None`. Random-access iterator behavior is not + /// affected, only single and double-ended iterator behavior. + /// + /// # Example + /// + /// ~~~ {.rust} + /// fn process>(it: U) -> int { + /// let mut it = it.fuse(); + /// let mut sum = 0; + /// for x in it { + /// if x > 5 { + /// break; + /// } + /// sum += x; + /// } + /// // did we exhaust the iterator? + /// if it.next().is_none() { + /// sum += 1000; + /// } + /// sum + /// } + /// let x = ~[1,2,3,7,8,9]; + /// assert_eq!(process(x.move_iter()), 1006); + /// ~~~ + #[inline] + fn fuse(self) -> Fuse { + Fuse{iter: self, done: false} + } + /// Creates an iterator that calls a function with a reference to each /// element before yielding it. This is often useful for debugging an /// iterator pipeline. @@ -892,9 +929,12 @@ pub struct Zip { impl, U: Iterator> Iterator<(A, B)> for Zip { #[inline] fn next(&mut self) -> Option<(A, B)> { - match (self.a.next(), self.b.next()) { - (Some(x), Some(y)) => Some((x, y)), - _ => None + match self.a.next() { + None => None, + Some(x) => match self.b.next() { + None => None, + Some(y) => Some((x, y)) + } } } @@ -925,9 +965,12 @@ RandomAccessIterator<(A, B)> for Zip { #[inline] fn idx(&self, index: uint) -> Option<(A, B)> { - match (self.a.idx(index), self.b.idx(index)) { - (Some(x), Some(y)) => Some((x, y)), - _ => None + match self.a.idx(index) { + None => None, + Some(x) => match self.b.idx(index) { + None => None, + Some(y) => Some((x, y)) + } } } } @@ -1421,6 +1464,79 @@ impl<'self, } } +/// An iterator that yields `None` forever after the underlying iterator +/// yields `None` once. +#[deriving(Clone, DeepClone)] +pub struct Fuse { + priv iter: T, + priv done: bool +} + +impl> Iterator for Fuse { + #[inline] + fn next(&mut self) -> Option { + if self.done { + None + } else { + match self.iter.next() { + None => { + self.done = true; + None + } + x => x + } + } + } + + #[inline] + fn size_hint(&self) -> (uint, Option) { + if self.done { + (0, Some(0)) + } else { + self.iter.size_hint() + } + } +} + +impl> DoubleEndedIterator for Fuse { + #[inline] + fn next_back(&mut self) -> Option { + if self.done { + None + } else { + match self.iter.next_back() { + None => { + self.done = true; + None + } + x => x + } + } + } +} + +// Allow RandomAccessIterators to be fused without affecting random-access behavior +impl> RandomAccessIterator for Fuse { + #[inline] + fn indexable(&self) -> uint { + self.iter.indexable() + } + + #[inline] + fn idx(&self, index: uint) -> Option { + self.iter.idx(index) + } +} + +impl Fuse { + /// Resets the fuse such that the next call to .next() or .next_back() will + /// call the underlying iterator again even if it prevously returned None. + #[inline] + fn reset_fuse(&mut self) { + self.done = false + } +} + /// An iterator that calls a function with a reference to each /// element before yielding it. pub struct Inspect<'self, A, T> {