В Rust вся стандартная коллекционная библиотека основана на концепции итераторов. Итератор — объект, реализующий трейт Iterator, в котором определён метод next(). Этот метод возвращает следующий элемент последовательности данных (Option<T>, где Some(T) — очередное значение, None — конец последовательности).
Пример стандартного итератора:
let v = vec![1, 2, 3]; let mut iter = v.iter(); while let Some(x) = iter.next() { println!("{}", x); }
Адаптеры итераторов — это методы (например, .map(), .filter(), .enumerate(), .take()) возвращающие новые итераторы, обрабатывающие значения «на лету» согласно переданным функциям.
Кастомные итераторы реализуются путём создания структуры и реализации для неё трейта Iterator с собственным поведением:
struct Counter { count: u8 } impl Iterator for Counter { type Item = u8; fn next(&mut self) -> Option<Self::Item> { if self.count < 5 { self.count += 1; Some(self.count) } else { None } } }
Используйте стандартные адаптеры, когда их достаточно для обработки коллекций. Реализация собственного итератора необходима, если:
Можно ли создавать бесконечные итераторы? Что произойдёт при попытке собрать их в коллекцию, например, с помощью
.collect()?
Ответ: Да, в Rust созданы итераторы вроде std::iter::repeat, возвращающие бесконечную последовательность:
let mut endless = std::iter::repeat(1); endless.next(); // возвратит Some(1) бесконечно
Если попытаться собрать такой итератор в коллекцию через .collect(), программа зависнет или упадёт с переполнением памяти, потому что итерация не завершится сама собой!
История
В проекте REST API массив из 1000 элементов сортировали, а затем использовали .iter().filter(|x| *x > 500), но вместо .collect::<Vec<_>>() применили .fold(0, |acc, _| acc + 1) внутри сложного адаптера, потеряв понимание о том, завершится ли итерация корректно. Итог: случайные зависания из-за того, что фильтрация происходила по неограниченной лени итератора с внутренней ошибкой.
История
В одном фирменном движке для генерации уникальных id случайный разработчик решил реализовать свой итератор, забыв возвращать None при достижении лимита. В результате итерация уходила в бесконечный цикл, сервер в продакшене потреблял всю CPU и не отвечал на запросы.
История
Во фронтенд-части (WebAssembly модуль) использовали итератор с вложенными адаптерами: .map().filter().skip(), и пытались получить результат через .collect() для формы. При изменении типа под адаптером Rust выдал сложную ошибку времени компиляции о несовпадении типов, из-за того, что забыли указать точный тип коллекции: .collect::<Vec<_>>(). Проблема решилась добавлением аннотации, но потратили несколько часов на поиски причины.