Iterators

take() and skip()

The .take(n) and .skip(n) adapters slice an iterator by element count: .take(n) yields at most the first n elements, and .skip(n) discards the first n elements before yielding the remainder.

Signatures

fn take(self, n: usize) -> Take<Self>
where
    Self: Sized;

fn skip(self, n: usize) -> Skip<Self>
where
    Self: Sized;
Source: [ 10,  20,  30,  40,  50,  60 ]
          └───┬───┘ └───────┬───┘
         .skip(2)        .take(3)
                            │
                            ▼
Output:             [ 30,  40,  50 ]

If the underlying iterator has fewer than n elements:

  • .take(n) yields all available elements and stops cleanly without panicking.
  • .skip(n) exhausts the iterator and yields 0 elements without panicking.

Pagination with .skip(offset).take(limit)

Combining .skip(offset) followed by .take(page_size) implements bounds-safe pagination over any iterator:

fn get_page<T: Copy>(items: &[T], page_index: usize, page_size: usize) -> Vec<T> {
    let offset = page_index * page_size;
    items.iter().copied().skip(offset).take(page_size).collect()
}

fn main() {
    let dataset = [10, 20, 30, 40, 50, 60, 70];

    println!("Page 0: {:?}", get_page(&dataset, 0, 3));
    println!("Page 1: {:?}", get_page(&dataset, 1, 3));
    println!("Page 2: {:?}", get_page(&dataset, 2, 3));
    println!("Page 9: {:?}", get_page(&dataset, 9, 3));
}
Page 0: [10, 20, 30]
Page 1: [40, 50, 60]
Page 2: [70]
Page 9: []

Bounding Infinite Iterators with .take(n)

Because Rust ranges without an upper bound (0..) and generators like std::iter::repeat and std::iter::successors produce infinite sequences, .take(n) is used to turn an infinite iterator into a finite one:

use std::iter::successors;

fn main() {
    // Powers of 2 starting at 1:
    let powers_of_two: Vec<u64> = successors(Some(1_u64), |&n| Some(n * 2))
        .take(8)
        .collect();

    println!("{powers_of_two:?}");
}
[1, 2, 4, 8, 16, 32, 64, 128]