Введение: Почему обратный цикл for в Rust — это ловушка для новичка?
Представьте ситуацию: вы переходите с Python на Rust. Всё вроде бы понятно, но когда нужно пройтись по массиву в обратном порядке — вы пишете for i in 0..10 и с удивлением обнаруживаете, что Rust идёт только вперед. Вы пытаетесь написать for i in 10..0 — и получаете пустой цикл. Знакомо? Если да, то вы не одиноки. Вопрос «How to make a reverse ordered for loop» на Stack Overflow собрал тысячи просмотров, и это неудивительно.
В этой статье мы разберём топ ошибок, которые допускают новички при работе с обратными циклами в Rust, и покажем, как их избежать. Вы узнаете, почему 10..0 не работает, как использовать метод .rev() и итераторы, а также как не сломать свой код при работе с векторами и срезами. Поехали!
Ошибка №1: Наивное использование диапазона в обратную сторону
Самая распространённая ошибка — попытка написать for i in 10..0 в надежде, что Rust поймёт: «а, пользователь хочет идти в обратном порядке». Увы, в Rust диапазон a..b всегда идёт от a до b не включительно. Если a >= b, то диапазон пуст.
// ❌ Ошибка: пустой цикл
for i in 10..0 {
println!("{}", i); // Ничего не выведется
}
Почему так? Rust — строгий язык. Диапазоны не умеют автоматически переворачиваться. Это сделано для производительности и предсказуемости. Если бы 10..0 работал как обратный, компилятору пришлось бы проверять каждое число на «а может, начальное значение больше конечного?», что замедлило бы обычные циклы.
Как исправить: используйте метод .rev()
Правильный способ — создать обычный возрастающий диапазон, а затем вызвать метод .rev(), который переворачивает итератор:
// ✅ Правильно: используем .rev()
for i in (0..10).rev() {
println!("{}", i); // Выведет: 9, 8, 7, ..., 0
}
Обратите внимание: мы используем (0..10).rev(), а не 10..0. Скобки обязательны, так как 0..10.rev() вызовет метод на 10, а не на диапазоне. Это ещё одна частая ошибка.
Ошибка №2: Забыли про скобки вокруг диапазона
Метод .rev() вызывается на итераторе. Диапазон 0..10 — это итератор. Но если написать 0..10.rev(), Rust интерпретирует это как 0..(10.rev()), а у целого числа 10 нет метода rev(). Компилятор выдаст ошибку:
// ❌ Ошибка компиляции: у числа нет метода rev()
for i in 0..10.rev() {
// error[E0599]: no method named `rev` found for integer `i32`
}
Решение простое — используйте круглые скобки:
// ✅ Скобки решают всё
for i in (0..10).rev() {
println!("{}", i);
}
Ошибка №3: Попытка перевернуть неитератор
Иногда новички пытаются применить .rev() к вектору или массиву напрямую:
let numbers = vec![1, 2, 3, 4, 5];
// ❌ Ошибка: у Vec нет метода rev()
for i in numbers.rev() {
println!("{}", i);
}
.rev() — это метод итератора, а не коллекции. Чтобы перевернуть вектор, нужно сначала получить итератор с помощью .iter() или .into_iter(), а затем вызвать .rev():
let numbers = vec![1, 2, 3, 4, 5];
// ✅ Правильно: получаем итератор, затем переворачиваем
for i in numbers.iter().rev() {
println!("{}", i); // Выведет: 5, 4, 3, 2, 1
}
Если вам нужно владеть элементами (например, переместить их), используйте .into_iter().rev():
let numbers = vec![1, 2, 3, 4, 5];
for i in numbers.into_iter().rev() {
println!("{}", i); // numbers теперь недоступен
}
Ошибка №4: Индексация массива в обратном порядке без итератора
Некоторые программисты, привыкшие к C/C++, пытаются использовать индексы вручную:
let arr = [10, 20, 30, 40, 50];
let len = arr.len();
// ❌ Не идиоматично, но работает
for i in 0..len {
println!("{}", arr[len - 1 - i]);
}
Это работает, но такой код сложнее читать. Rust предлагает более элегантный способ — использовать .iter().rev():
let arr = [10, 20, 30, 40, 50];
// ✅ Идиоматичный Rust: используем итератор
for value in arr.iter().rev() {
println!("{}", value);
}
Плюсы: код короче, не нужно думать об индексах, меньше шансов ошибиться на единицу (off-by-one error).
Ошибка №5: Игнорирование метода .rev() для строк
Строки — это не векторы, но к ним тоже можно применить обратный итератор. Однако новички часто пытаются перевернуть строку через .chars().rev() и удивляются результату:
let s = "привет";
// ✅ Переворачиваем символы
for c in s.chars().rev() {
print!("{}", c); // Выведет: тевирп
}
Всё работает! Но будьте осторожны: .chars() возвращает Unicode-скаляры, а не байты. Если вы хотите перевернуть строку как байтовый массив, используйте .bytes().rev():
let s = "hello";
for b in s.bytes().rev() {
print!("{} ", b); // Выведет: 111 108 108 101 104
}
Ошибка №6: Забываем про метод .rev() в цепочках итераторов
Rust позволяет комбинировать итераторы. Например, вы можете отфильтровать элементы, а затем перевернуть результат:
let numbers = vec![1, 2, 3, 4, 5, 6];
// ❌ Неправильный порядок: фильтр после rev()
for i in numbers.iter().filter(|&&x| x % 2 == 0).rev() {
println!("{}", i); // Ошибка: filter не возвращает перевора