Как итерироваться по диапазону с нестандартным шагом в Rust?
Использование метода step_by() из трейта Iterator
Проблема в вашем коде — приоритет операций. Вы пишете 0..=100.step_by(5), что интерпретируется как 0..=(100.step_by(5)). Rust пытается вызвать step_by на числе 100, а не на диапазоне. Нужно взять весь диапазон в скобки:
fn main() {
for i in (0..=100).step_by(5) {
println!("{}", i);
}
}
Теперь всё работает! step_by(5) — это метод трейта Iterator, который доступен для любого итератора, в том числе для диапазонов Range и RangeInclusive. Он создаёт новый итератор, который пропускает каждые n-1 элементов.
Как это работает под капотом
Метод step_by() возвращает структуру StepBy, которая реализует трейт Iterator. Она хранит исходный итератор и счётчик шагов. При каждом вызове next() она пропускает нужное количество элементов и возвращает следующий.
Важные замечания
- Направление итерации:
step_by()работает только с итераторами, которые движутся вперёд. Для обратного хода используйтеrev()передstep_by(). - Шаг должен быть положительным:
step_by(0)вызовет панику. Минимальное значение — 1. - Работа с включительными диапазонами:
(0..=100).step_by(5)выдаст 0, 5, ..., 100. Если шаг не кратен границе, последний элемент может не войти. - Производительность:
step_byработает за O(1) по памяти и O(n/m) по времени, где n — размер диапазона, m — шаг.
Примеры для разных сценариев
// Шаг 3 от 1 до 10 (не включая 10)
for i in (1..10).step_by(3) {
println!("{}", i); // 1, 4, 7
}
// Обратный порядок с шагом 2
for i in (0..=10).rev().step_by(2) {
println!("{}", i); // 10, 8, 6, 4, 2, 0
}
// Работа с массивами через индексы
let arr = [10, 20, 30, 40, 50];
for i in (0..arr.len()).step_by(2) {
println!("arr[{}] = {}", i, arr[i]); // arr[0]=10, arr[2]=30, arr[4]=50
}
Альтернативный подход: фильтрация
Если вам нужна более сложная логика шага, можно использовать filter():
for i in (0..=100).filter(|x| x % 5 == 0) {
println!("{}", i);
}
Но для простых арифметических шагов step_by() эффективнее.
Итог
Главное — не забывайте скобки вокруг диапазона. В Rust step_by() — это стандартный метод итератора, он стабилен и работает начиная с Rust 1.28. Используйте его для любых последовательностей с равномерным шагом.
Создание собственного итератора с шагом
Если по какой-то причине step_by() не подходит (например, нужен нелинейный шаг или работа с очень старым Rust), можно написать свой итератор вручную. Это также полезно для понимания того, как работают итераторы в Rust.
struct StepRange {
current: i32,
end: i32,
step: i32,
}
impl Iterator for StepRange {
type Item = i32;
fn next(&mut self) -> Option<Self::Item> {
if self.current > self.end {
None
} else {
let result = self.current;
self.current += self.step;
Some(result)
}
}
}
fn main() {
let range = StepRange { current: 0, end: 100, step: 5 };
for i in range {
println!("{}", i);
}
}
Этот подход даёт полный контроль над логикой итерации. Вы можете добавить поддержку разных типов через дженерики, проверку границ и т.д. Однако для 95% случаев встроенный step_by() предпочтительнее — он короче, стандартизирован и оптимизирован.
Ещё один вариант — использовать крейт itertools, который предоставляет множество полезных методов для итераторов, включая step(). Но для такой простой задачи подключать внешнюю зависимость обычно излишне.
Ответов пока нет.
Войдите чтобы оставить ответ