Перейти к содержимому

Цикл for в Rust

for проходит по диапазону или коллекции, по одному элементу за раз. Из трёх видов циклов в Rust именно им ты будешь пользоваться чаще всего.

fn main() {
for i in 1..=5 {
println!("{i}");
}
}

Вывод:

1
2
3
4
5
▶ Попробуй сам

1..=5 — это диапазон (range), который включает оба конца (от 1 до 5). Если убрать = и написать 1..5, он остановится на шаг раньше и даст числа от 1 до 4. На этой разнице в единицу легко споткнуться, так что стоит перепроверять, что именно ты имел в виду.

Вот где for по-настоящему хорош — перебор массива или вектора без ручного подсчёта индекса:

fn main() {
let fruits = ["яблоко", "банан", "вишня"];
for fruit in fruits {
println!("{fruit}");
}
}

Вывод:

яблоко
банан
вишня
▶ Попробуй сам

Сравни это с ручным счётчиком в цикле while и обращением по индексу (fruits[i]): так короче, и нет индекса, в котором можно ошибиться. Подробнее о массивах и векторах — в уроках Массивы и Векторы.

Если нужны и позиция, и значение, используй .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.

Цикл со счётчиком можно написать и через while, но for с диапазоном убирает целый класс ошибок: нет ручного счётчика, который можно забыть обновить, и нет риска шагнуть за конец массива. Программисты на Rust по умолчанию берут for, а while используют, только когда нет естественного диапазона или коллекции для перебора.

Выведи таблицу умножения на 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 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35
7 x 6 = 42
7 x 7 = 49
7 x 8 = 56
7 x 9 = 63
7 x 10 = 70
▶ Попробуй сам

Имена переменных вроде {i} можно писать прямо в фигурных скобках. А вычисление вроде 7 * i — нельзя, поэтому для него стоят пустые {}, а значение идёт после запятой.

Сложи все нечётные числа от 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