Итераторы

Что такое итератор в Rust?

Итератор в Rust — это трейт (std::iter::Iterator).

Трейт Iterator:

#![allow(unused)]
fn main() {
pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    // ... множество предоставляемых методов по умолчанию
}
}

Создание итераторов:

1. Из коллекций (реализуют трейт IntoIterator):

#![allow(unused)]
fn main() {
let vec = vec![1, 2, 3];
let iter = vec.iter();        // Итератор по &T
let iter_mut = vec.iter_mut(); // Итератор по &mut T  
let into_iter = vec.into_iter(); // Итератор, потребляющий коллекцию (T)
}

2. Диапазоны:

#![allow(unused)]
fn main() {
let range = 1..10;           // 1 до 9
let range_inclusive = 1..=10; // 1 до 10
}

3. Адаптеры итераторов:

#![allow(unused)]
fn main() {
let mapped = vec.iter().map(|x| x * 2);
let filtered = vec.iter().filter(|x| *x % 2 == 0);
}

4. Создание кастомных итераторов:

#![allow(unused)]
fn main() {
struct Counter {
    count: u32,
}

impl Iterator for Counter {
    type Item = u32;
    
    fn next(&mut self) -> Option<Self::Item> {
        if self.count < 5 {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}
}

Организация в памяти:

Размер на стеке:

  • Итераторы обычно имеют фиксированный размер во время компиляции
  • Не требуют heap-аллокации (если не содержат Box и т.д.)
  • Пример:
#![allow(unused)]
fn main() {
let iter = vec![1, 2, 3].into_iter();
// Размер известен компилятору, хранится на стеке
}

Типы итераторов:

  • By-reference: iter()std::slice::Iter<'a, T>
  • By-mutable-reference: iter_mut()std::slice::IterMut<'a, T>
  • By-value: into_iter()std::vec::IntoIter<T>

Потребители итераторов:

1. Явное использование next():

#![allow(unused)]
fn main() {
let mut iter = vec![1, 2, 3].iter();
while let Some(item) = iter.next() {
    println!("{}", item);
}
}

2. Цикл for (синтаксический сахар):

#![allow(unused)]
fn main() {
for item in vec.iter() {
    println!("{}", item);  // Автоматически вызывает next()
}
}

3. Методы-потребители:

#![allow(unused)]
fn main() {
let sum: i32 = vec.iter().sum();           // Потребляет итератор
let collected: Vec<_> = iter.collect();    // Потребляет итератор
let count = iter.count();                  // Потребляет итератор
}

4. Адаптеры (ленивые, не потребляют сразу):

#![allow(unused)]
fn main() {
let result: Vec<_> = vec![1, 2, 3]
    .iter()
    .map(|x| x * 2)        // Ленивый - не выполняется до потребления
    .filter(|x| x > &2)    // Ленивый
    .collect();            // Потребляет, форсирует вычисления
}

Ключевые особенности:

Ленивость:

#![allow(unused)]
fn main() {
let iter = vec![1, 2, 3].iter().map(|x| {
    println!("Processing: {}", x);
    x * 2
});
// Ничего не напечатано - вычисления не начались

let result: Vec<_> = iter.collect(); 
// Теперь вычисления выполнены
}

Zero-cost abstractions:

  • Итераторы компилируются в эффективный код
  • Часто сопоставим с ручным циклом по производительности

Типовая система:

#![allow(unused)]
fn main() {
fn process<I: Iterator<Item = i32>>(iter: I) {
    for item in iter {
        println!("{}", item);
    }
}
}

Итератор в Rust — это трейт, который предоставляет поток элементов, с ленивыми вычислениями и эффективной компиляцией в оптимальный машинный код.