Цикл for в Rust
for проходит по диапазону или коллекции, по одному элементу за раз. Из трёх видов циклов в Rust именно им ты будешь пользоваться чаще всего.
Перебираем диапазон
Заголовок раздела «Перебираем диапазон»fn main() { for i in 1..=5 { println!("{i}"); }}Вывод:
123451..=5 — это диапазон (range), который включает оба конца (от 1 до 5). Если убрать = и написать 1..5, он остановится на шаг раньше и даст числа от 1 до 4. На этой разнице в единицу легко споткнуться, так что стоит перепроверять, что именно ты имел в виду.
Перебираем коллекцию
Заголовок раздела «Перебираем коллекцию»Вот где for по-настоящему хорош — перебор массива или вектора без ручного подсчёта индекса:
fn main() { let fruits = ["яблоко", "банан", "вишня"];
for fruit in fruits { println!("{fruit}"); }}Вывод:
яблокобананвишняСравни это с ручным счётчиком в цикле while и обращением по индексу (fruits[i]): так короче, и нет индекса, в котором можно ошибиться. Подробнее о массивах и векторах — в уроках Массивы и Векторы.
Индекс в придачу: .enumerate()
Заголовок раздела «Индекс в придачу: .enumerate()»Если нужны и позиция, и значение, используй .enumerate():
fn main() { let fruits = ["яблоко", "банан", "вишня"];
for (index, fruit) in fruits.iter().enumerate() { println!("{index}: {fruit}"); }}Вывод:
0: яблоко1: банан2: вишняЧасть (index, fruit) — это образец для кортежа: на каждом шаге ты получаешь пару, и цикл сам раскладывает её по двум переменным.
Считаем назад и с шагом
Заголовок раздела «Считаем назад и с шагом»А если нужно считать вниз от 3 или брать только каждое второе число? Первое, что приходит в голову, — диапазон наоборот, например 3..=1. Он компилируется, но цикл ни разу не выполнится. Диапазон считает только вверх, поэтому диапазон, у которого начало больше конца, просто пустой.
Вместо этого возьми обычный диапазон и измени способ, которым ты его проходишь:
.rev()проходит диапазон в обратном порядке..step_by(n)берёт каждоеn-е значение, начиная с первого.
fn main() { for i in (1..=3).rev() { println!("{i}..."); } println!("Старт!");
for even in (0..=10).step_by(2) { print!("{even} "); } println!();
for i in 3..=1 { println!("это никогда не выведется: {i}"); }}Вывод:
3...2...1...Старт!0 2 4 6 8 10Обрати внимание на скобки: (1..=3).rev(). Без них Rust прочитал бы 1..=3.rev() как «от 1 до 3.rev()», а это совсем не то. Оба приёма можно сочетать: (0..=10).rev().step_by(5) даёт 10, 5, 0.
Почему для подсчёта for лучше, чем while
Заголовок раздела «Почему для подсчёта for лучше, чем while»Цикл со счётчиком можно написать и через while, но for с диапазоном убирает целый класс ошибок: нет ручного счётчика, который можно забыть обновить, и нет риска шагнуть за конец массива. Программисты на Rust по умолчанию берут for, а while используют, только когда нет естественного диапазона или коллекции для перебора.
Практика
Заголовок раздела «Практика»1. Таблица умножения
Заголовок раздела «1. Таблица умножения»Выведи таблицу умножения на 7 от 7 x 1 = 7 до 7 x 10 = 70. Подсказка: хватит одного цикла for по 1..=10. Умножение можно сделать прямо внутри println!.
Показать решение
fn main() { for i in 1..=10 { println!("7 x {i} = {}", 7 * i); }}Вывод:
7 x 1 = 77 x 2 = 147 x 3 = 217 x 4 = 287 x 5 = 357 x 6 = 427 x 7 = 497 x 8 = 567 x 9 = 637 x 10 = 70Имена переменных вроде {i} можно писать прямо в фигурных скобках. А вычисление вроде 7 * i — нельзя, поэтому для него стоят пустые {}, а значение идёт после запятой.
2. Сумма нечётных чисел
Заголовок раздела «2. Сумма нечётных чисел»Сложи все нечётные числа от 1 до 99 и выведи сумму. Подсказка: (1..100).step_by(2) даёт 1, 3, 5, … 99. Заведи mut-сумму снаружи цикла.
Показать решение
fn main() { let mut total = 0;
for n in (1..100).step_by(2) { total += n; }
println!("Сумма нечётных чисел от 1 до 99 равна {total}");}Вывод:
Сумма нечётных чисел от 1 до 99 равна 2500Можно и по-другому: перебрать 1..100 и прибавлять n, только когда n % 2 == 1. Работают оба способа. step_by просто пропускает чётные числа, вместо того чтобы проверять каждое.
for элемент in диапазон_или_коллекция { ... }— повседневный цикл в Rust.1..=5включает оба конца;1..5останавливается, не дойдя до 5.- Перебирай массивы и векторы напрямую — так получаешь каждое значение без ручной работы с индексами.
- Используй
.enumerate(), когда вместе со значением нужен индекс. - Диапазон наоборот вроде
3..=1пустой. Для обратного отсчёта пиши(1..=3).rev(), а для шага —.step_by(n).
Проверь себя
Заголовок раздела «Проверь себя»Проверь себя
1. Чем 1..=5 отличается от 1..5 в цикле for?
2. Как получить и индекс, и значение при переборе коллекции?
3. Почему для перебора коллекции обычно предпочитают for, а не while?
4. Что делает for i in 5..=1 { ... }?
Результат: 0 / 4
Что почитать
Заголовок раздела «Что почитать»- Цикл по элементам коллекции с помощью for — книга «Язык программирования Rust»: официальное введение, в том числе обратный отсчёт через
.rev()(перевод на русский). - for и range — Rust на примерах: запускаемые примеры с диапазонами, а также
iter,iter_mutиinto_iterдля коллекций (перевод на русский). - Iterator loops — The Rust Reference (на английском): точные правила того, что цикл
forделает «под капотом». Iterator::step_by— документация стандартной библиотеки (на английском): как работаетstep_by, с примерами.revописан на той же странице.