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

Цикл while в Rust

Цикл while работает, пока условие остаётся true. Это подходящий инструмент, когда ты знаешь, чего ждёшь, даже если не знаешь точно, сколько раз придётся повторить.

fn main() {
let mut count = 1;
while count <= 5 {
println!("{count}");
count += 1;
}
}

Вывод:

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

Условие (count <= 5) проверяется перед каждым проходом цикла. Как только оно становится false, цикл заканчивается — здесь это происходит, когда count становится 6.

То же самое можно написать через loop и ручной if/break, но while выражает мысль прямее: «продолжай, пока это истинно». Оставь loop для случаев, когда условие остановки неудобно ставить в начало цикла.

// через while — ясно и по делу
let mut n = 0;
while n < 3 {
println!("{n}");
n += 1;
}
// то же самое через loop — работает, но читать дольше
let mut n = 0;
loop {
if n >= 3 {
break;
}
println!("{n}");
n += 1;
}

while часто используют, чтобы дождаться, пока значение дойдёт до цели:

fn main() {
let mut balance = 100;
while balance < 200 {
balance += 25;
println!("Теперь на счёте {balance}");
}
println!("Цель достигнута!");
}

Вывод:

Теперь на счёте 125
Теперь на счёте 150
Теперь на счёте 175
Теперь на счёте 200
Цель достигнута!
▶ Попробуй сам

Условием может быть любое выражение, которое даёт bool, поэтому проверки можно объединять через && и || из урока Операторы, например while balance < 200 && months < 12.

В некоторых языках есть цикл do { ... } while (condition). Он сначала выполняет тело, а потом проверяет условие, так что тело всегда выполняется хотя бы один раз. В Rust такого цикла нет. Вместо него используют loop и ставят проверку в конец.

Вот где это важно. Чтобы посчитать цифры в числе, можно делить его на 10, пока ничего не останется. Но у числа 0 тоже одна цифра, а обычный цикл while для него не выполнится ни разу:

fn main() {
let number = 0;
// while сначала проверяет условие, поэтому для 0 не выполняется ни разу.
let mut n = number;
let mut digits = 0;
while n > 0 {
digits += 1;
n /= 10;
}
println!("while: в числе {number} цифр: {digits}");
// loop с проверкой в конце выполняет тело хотя бы один раз.
let mut n = number;
let mut digits = 0;
loop {
digits += 1;
n /= 10;
if n == 0 {
break;
}
}
println!("loop: в числе {number} цифр: {digits}");
}

Вывод:

while: в числе 0 цифр: 0
loop: в числе 0 цифр: 1
▶ Попробуй сам

Замени number на 4096 — и обе версии согласятся: 4 цифры. Разница видна только тогда, когда условие ложно с самого начала. Отсюда простое правило: если тело должно выполниться хотя бы раз, пиши loop с if ... { break; } внизу.

Есть ещё одна разновидность цикла while — while let, он работает, пока значение подходит под образец. О нём рассказано в уроке if let и while let.

Складывай 1 + 2 + 3 + … и остановись, как только сумма станет больше 100. Выведи, сколько чисел ты сложил, и саму сумму. Подсказка: нужны две mut-переменные: одна для следующего числа, другая для суммы.

Показать решение
fn main() {
let mut next = 1;
let mut total = 0;
while total <= 100 {
total += next;
next += 1;
}
println!("Сложили чисел: {}, сумма: {total}", next - 1);
}

Вывод:

Сложили чисел: 14, сумма: 105
▶ Попробуй сам

После последнего прохода next уже ушла на шаг вперёд, поэтому мы выводим next - 1. Такие ошибки «на единицу» стоит проверять на маленьком примере в уме.

Начни с 1000. Пока число больше 1, дели его на 2 (целочисленно) и выводи. В конце выведи, сколько понадобилось делений. Подсказка: n /= 2 делит n на 2 и отбрасывает остаток.

Показать решение
fn main() {
let mut n = 1000;
let mut steps = 0;
while n > 1 {
n /= 2;
steps += 1;
println!("{n}");
}
println!("Чтобы дойти до 1, понадобилось делений: {steps}");
}

Вывод:

500
250
125
62
31
15
7
3
1
Чтобы дойти до 1, понадобилось делений: 9
▶ Попробуй сам

125 / 2 — это 62, а не 62.5, потому что оба числа целые. Именно поэтому цикл заканчивается ровно на 1.

  • while условие { ... } повторяется, пока условие true; проверка идёт перед каждым проходом.
  • Убедись, что что-то внутри цикла рано или поздно сделает условие false, иначе он будет работать вечно.
  • Предпочитай while, а не loop, когда условие остановки естественно звучит как «пока выполняется X».
  • В Rust нет do-while. Если тело должно выполниться хотя бы раз, используй loop с проверкой break в конце.

Проверь себя

1. Какая классическая ошибка приводит к бесконечному циклу while?

2. Когда, судя по этой странице, стоит предпочесть while, а не loop?

3. Как написать цикл, тело которого всегда выполняется хотя бы один раз?

Результат: 0 / 3