Skip to content

Commit c4b92ca

Browse files
Itertools::tail: skip the starting part of the iterator
If the iterator is exact sized, then `.collect()` finishes the work. More generally, if the size hint knows enough and `nth` is efficient, this might skip most of the iterator efficiently. In the tests, `.filter(..)` is there to ensure that `tail` can't leverage a precise `size_hint` to entirely skip the iteration after initial `.collect()`. Co-Authored-By: scottmcm <[email protected]>
1 parent 2071e2a commit c4b92ca

File tree

2 files changed

+8
-2
lines changed

2 files changed

+8
-2
lines changed

src/lib.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -3146,6 +3146,8 @@ pub trait Itertools: Iterator {
31463146
/// assert_equal(v.iter().tail(10), &v);
31473147
///
31483148
/// assert_equal((0..100).tail(10), 90..100);
3149+
///
3150+
/// assert_equal((0..100).filter(|x| x % 3 == 0).tail(10), (72..100).step_by(3));
31493151
/// ```
31503152
///
31513153
/// For double ended iterators without side-effects, you might prefer
@@ -3163,7 +3165,9 @@ pub trait Itertools: Iterator {
31633165
}
31643166
1 => self.last().into_iter().collect(),
31653167
_ => {
3166-
let mut iter = self.fuse();
3168+
// Skip the starting part of the iterator if possible.
3169+
let (low, _) = self.size_hint();
3170+
let mut iter = self.fuse().skip(low.saturating_sub(n));
31673171
let mut data: Vec<_> = iter.by_ref().take(n).collect();
31683172
// Update `data` cyclically.
31693173
let idx = iter.fold(0, |i, val| {

tests/quick.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1952,6 +1952,8 @@ quickcheck! {
19521952

19531953
fn tail(v: Vec<i32>, n: u8) -> bool {
19541954
let n = n as usize;
1955-
itertools::equal(v.iter().tail(n), &v[v.len().saturating_sub(n)..])
1955+
let result = &v[v.len().saturating_sub(n)..];
1956+
itertools::equal(v.iter().tail(n), result)
1957+
&& itertools::equal(v.iter().filter(|_| true).tail(n), result)
19561958
}
19571959
}

0 commit comments

Comments
 (0)