Skip to content

Commit

Permalink
Rollup merge of rust-lang#60130 - khuey:efficient_last, r=sfackler
Browse files Browse the repository at this point in the history
Add implementations of last in terms of next_back on a bunch of DoubleEndedIterators

Provided a `DoubleEndedIterator` has finite length, `Iterator::last` is equivalent to `DoubleEndedIterator::next_back`. But searching forwards through the iterator when it's unnecessary is obviously not good for performance. I ran into this on one of the collection iterators.

I tried adding appropriate overloads for a bunch of the iterator adapters like filter, map, etc, but I ran into a lot of type inference failures after doing so.

The other interesting case is what to do with `Repeat`. Do we consider it part of the contract that `Iterator::last` will loop forever on it? The docs do say that the iterator will be evaluated until it returns None. This is also relevant for the adapters, it's trivially easy to observe whether a `Map` adapter invoked its closure a zillion times or just once for the last element.
  • Loading branch information
Centril authored May 14, 2019
2 parents f59c71e + 3e86cf3 commit bab03ce
Show file tree
Hide file tree
Showing 14 changed files with 159 additions and 0 deletions.
15 changes: 15 additions & 0 deletions src/liballoc/collections/binary_heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -992,6 +992,11 @@ impl<'a, T> Iterator for Iter<'a, T> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}

#[inline]
fn last(mut self) -> Option<&'a T> {
self.next_back()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -1047,6 +1052,11 @@ impl<T> Iterator for IntoIter<T> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}

#[inline]
fn last(mut self) -> Option<T> {
self.next_back()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -1093,6 +1103,11 @@ impl<T> Iterator for Drain<'_, T> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}

#[inline]
fn last(mut self) -> Option<T> {
self.next_back()
}
}

#[stable(feature = "drain", since = "1.6.0")]
Expand Down
40 changes: 40 additions & 0 deletions src/liballoc/collections/btree/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,11 @@ impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> {
fn size_hint(&self) -> (usize, Option<usize>) {
(self.length, Some(self.length))
}

#[inline]
fn last(mut self) -> Option<(&'a K, &'a V)> {
self.next_back()
}
}

#[stable(feature = "fused", since = "1.26.0")]
Expand Down Expand Up @@ -1253,6 +1258,11 @@ impl<'a, K: 'a, V: 'a> Iterator for IterMut<'a, K, V> {
fn size_hint(&self) -> (usize, Option<usize>) {
(self.length, Some(self.length))
}

#[inline]
fn last(mut self) -> Option<(&'a K, &'a mut V)> {
self.next_back()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -1359,6 +1369,11 @@ impl<K, V> Iterator for IntoIter<K, V> {
fn size_hint(&self) -> (usize, Option<usize>) {
(self.length, Some(self.length))
}

#[inline]
fn last(mut self) -> Option<(K, V)> {
self.next_back()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -1421,6 +1436,11 @@ impl<'a, K, V> Iterator for Keys<'a, K, V> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}

#[inline]
fn last(mut self) -> Option<&'a K> {
self.next_back()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -1458,6 +1478,11 @@ impl<'a, K, V> Iterator for Values<'a, K, V> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}

#[inline]
fn last(mut self) -> Option<&'a V> {
self.next_back()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -1495,6 +1520,11 @@ impl<'a, K, V> Iterator for Range<'a, K, V> {
unsafe { Some(self.next_unchecked()) }
}
}

#[inline]
fn last(mut self) -> Option<(&'a K, &'a V)> {
self.next_back()
}
}

#[stable(feature = "map_values_mut", since = "1.10.0")]
Expand All @@ -1508,6 +1538,11 @@ impl<'a, K, V> Iterator for ValuesMut<'a, K, V> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}

#[inline]
fn last(mut self) -> Option<&'a mut V> {
self.next_back()
}
}

#[stable(feature = "map_values_mut", since = "1.10.0")]
Expand Down Expand Up @@ -1626,6 +1661,11 @@ impl<'a, K, V> Iterator for RangeMut<'a, K, V> {
unsafe { Some(self.next_unchecked()) }
}
}

#[inline]
fn last(mut self) -> Option<(&'a K, &'a mut V)> {
self.next_back()
}
}

impl<'a, K, V> RangeMut<'a, K, V> {
Expand Down
15 changes: 15 additions & 0 deletions src/liballoc/collections/btree/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,11 @@ impl<'a, T> Iterator for Iter<'a, T> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}

#[inline]
fn last(mut self) -> Option<&'a T> {
self.next_back()
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T> DoubleEndedIterator for Iter<'a, T> {
Expand All @@ -1044,6 +1049,11 @@ impl<T> Iterator for IntoIter<T> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}

#[inline]
fn last(mut self) -> Option<T> {
self.next_back()
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> DoubleEndedIterator for IntoIter<T> {
Expand Down Expand Up @@ -1073,6 +1083,11 @@ impl<'a, T> Iterator for Range<'a, T> {
fn next(&mut self) -> Option<&'a T> {
self.iter.next().map(|(k, _)| k)
}

#[inline]
fn last(mut self) -> Option<&'a T> {
self.next_back()
}
}

#[stable(feature = "btree_range", since = "1.17.0")]
Expand Down
4 changes: 4 additions & 0 deletions src/liballoc/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2377,6 +2377,10 @@ impl Iterator for Drain<'_> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
#[inline]
fn last(mut self) -> Option<char> {
self.next_back()
}
}

#[stable(feature = "drain", since = "1.6.0")]
Expand Down
14 changes: 14 additions & 0 deletions src/liballoc/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2395,6 +2395,11 @@ impl<T> Iterator for IntoIter<T> {
fn count(self) -> usize {
self.len()
}

#[inline]
fn last(mut self) -> Option<T> {
self.next_back()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -2514,6 +2519,11 @@ impl<T> Iterator for Drain<'_, T> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}

#[inline]
fn last(mut self) -> Option<T> {
self.next_back()
}
}

#[stable(feature = "drain", since = "1.6.0")]
Expand Down Expand Up @@ -2583,6 +2593,10 @@ impl<I: Iterator> Iterator for Splice<'_, I> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.drain.size_hint()
}

fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}

#[stable(feature = "vec_splice", since = "1.21.0")]
Expand Down
2 changes: 2 additions & 0 deletions src/libcore/ascii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ impl Iterator for EscapeDefault {
type Item = u8;
fn next(&mut self) -> Option<u8> { self.range.next().map(|i| self.data[i]) }
fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
#[inline]
fn last(mut self) -> Option<u8> { self.next_back() }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl DoubleEndedIterator for EscapeDefault {
Expand Down
5 changes: 5 additions & 0 deletions src/libcore/iter/adapters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ impl<I> Iterator for Rev<I> where I: DoubleEndedIterator {
{
self.iter.position(predicate)
}

#[inline]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down
20 changes: 20 additions & 0 deletions src/libcore/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3541,6 +3541,11 @@ impl<'a, T, P> Iterator for Split<'a, T, P> where P: FnMut(&T) -> bool {
(1, Some(self.v.len() + 1))
}
}

#[inline]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -3639,6 +3644,11 @@ impl<'a, T, P> Iterator for SplitMut<'a, T, P> where P: FnMut(&T) -> bool {
(1, Some(self.v.len() + 1))
}
}

#[inline]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -3704,6 +3714,11 @@ impl<'a, T, P> Iterator for RSplit<'a, T, P> where P: FnMut(&T) -> bool {
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}

#[inline]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}

#[stable(feature = "slice_rsplit", since = "1.27.0")]
Expand Down Expand Up @@ -3768,6 +3783,11 @@ impl<'a, T, P> Iterator for RSplitMut<'a, T, P> where P: FnMut(&T) -> bool {
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}

#[inline]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}

#[stable(feature = "slice_rsplit", since = "1.27.0")]
Expand Down
20 changes: 20 additions & 0 deletions src/libcore/str/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1333,6 +1333,11 @@ impl<'a> Iterator for Lines<'a> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}

#[inline]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -1379,6 +1384,11 @@ impl<'a> Iterator for LinesAny<'a> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}

#[inline]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -4217,6 +4227,11 @@ impl<'a> Iterator for SplitWhitespace<'a> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}

#[inline]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}

#[stable(feature = "split_whitespace", since = "1.1.0")]
Expand All @@ -4243,6 +4258,11 @@ impl<'a> Iterator for SplitAsciiWhitespace<'a> {
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}

#[inline]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}

#[stable(feature = "split_ascii_whitespace", since = "1.34.0")]
Expand Down
6 changes: 6 additions & 0 deletions src/libstd/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,10 @@ impl Iterator for Args {
self.inner.next().map(|s| s.into_string().unwrap())
}
fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() }
#[inline]
fn last(mut self) -> Option<String> {
self.next_back()
}
}

#[stable(feature = "env", since = "1.0.0")]
Expand Down Expand Up @@ -781,6 +785,8 @@ impl Iterator for ArgsOs {
type Item = OsString;
fn next(&mut self) -> Option<OsString> { self.inner.next() }
fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() }
#[inline]
fn last(mut self) -> Option<OsString> { self.next_back() }
}

#[stable(feature = "env", since = "1.0.0")]
Expand Down
10 changes: 10 additions & 0 deletions src/libstd/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,11 @@ impl<'a> Iterator for Iter<'a> {
fn next(&mut self) -> Option<&'a OsStr> {
self.inner.next().map(Component::as_os_str)
}

#[inline]
fn last(mut self) -> Option<&'a OsStr> {
self.next_back()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -951,6 +956,11 @@ impl<'a> Iterator for Components<'a> {
}
None
}

#[inline]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down
2 changes: 2 additions & 0 deletions src/libstd/sys/unix/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> { self.iter.next() }
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
#[inline]
fn last(mut self) -> Option<OsString> { self.next_back() }
}

impl ExactSizeIterator for Args {
Expand Down
4 changes: 4 additions & 0 deletions src/libstd/sys/wasm/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ impl Iterator for Args {
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
#[inline]
fn last(mut self) -> Option<OsString> {
self.next_back()
}
}

impl ExactSizeIterator for Args {
Expand Down
Loading

0 comments on commit bab03ce

Please sign in to comment.