В цикле ниже items используется после обхода. Какой механизм Rust делает переменную недоступной после цикла?
fn main() {
let items = vec![String::from("a"), String::from("b")];
for item in items {
println!("{item}");
}
println!("{}", items.len());
}
for item in items передаёт items во владение итератору через IntoIterator::into_iter. Для Vec<T> такой итератор владеет вектором и перемещает его элементы по одному, поэтому после цикла переменная items больше недоступна.
Если нужно сохранить доступ к вектору, его следует обходить по ссылке: for item in &items или for item in items.iter().
Модель владения в Rust решает задачу безопасного управления памятью без сборщика мусора и без ручного освобождения каждого объекта. Компилятор должен точно знать, кто отвечает за ресурс и когда его можно освободить.
Циклы по коллекциям подчиняются тем же правилам, что и обычные вызовы функций: передача значения без ссылки может передать владение. Это позволяет безопасно обрабатывать элементы, не создавая неявных копий и не оставляя неоднозначным владельца коллекции.
В коде items передаётся в цикл по значению. Итератор получает владение всем Vec<String>, а затем выдаёт его элементы циклу; каждый String перемещается в переменную item.
После передачи владения исходное имя нельзя использовать. Попытка вызвать items.len() приводит к ошибке компиляции «use of moved value», потому что Rust не может одновременно считать вектор перемещённым и доступным через прежнюю переменную.
Конструкция цикла логически основана на вызове IntoIterator::into_iter:
В исходном варианте выражение items используется по значению. Для Vec<T> это означает создание владеющего итератора: сам вектор перемещается в итератор, а его элементы затем передаются в item по одному. Для String это именно перемещение, а не копирование содержимого строки.
При обходе &items в into_iter передаётся ссылка на вектор. Итератор выдаёт &String, не забирая владение ни у вектора, ни у его элементов. Поэтому после завершения цикла items остаётся доступным.
Выбор формы обхода определяет права внутри тела цикла:
for item in items — владение элементами перемещается в цикл; исходный вектор после этого недоступен;for item in &items — элементы доступны только для чтения через &String;for item in &mut items — элементы доступны для изменения через &mut String, но вектор временно занят изменяемым заимствованием.Если коллекция больше не нужна после цикла, потребляющий обход может быть правильным и эффективным: он не требует клонирования элементов. Если коллекция должна использоваться дальше, заимствующий обход обычно предпочтительнее; клонирование является запасным вариантом, но создаёт дополнительные расходы и требует, чтобы элементы реализовывали Clone.
Предположим, обработчик получает Vec<String>, печатает все значения, а затем записывает количество элементов в метрики. Вариант for item in items удобен, если после печати коллекция действительно больше не нужна, но он не позволяет обратиться к items.len() после цикла.
Можно клонировать вектор перед обходом, однако это дублирует все строки и увеличивает потребление памяти. Можно отдельно сохранить длину до цикла, но это подходит только для конкретной метрики и не сохраняет доступ к самой коллекции.
Рациональное решение — for item in &items: обработчик читает строки без копирования, а после цикла использует items.len(). Если обработка должна изменять элементы, применяется for item in &mut items; при этом нельзя одновременно выполнять независимую операцию, требующую другого доступа к вектору, пока активен такой обход.
Чем отличается for item in items от for item in items.into_iter()?
Для Vec<T> по смыслу это один и тот же владеющий обход: вызывается IntoIterator::into_iter для самого вектора. В обоих случаях вектор перемещается в итератор, а элементы выдаются по значению. Явная запись полезна для объяснения механизма, но не меняет владение.
Можно ли использовать items после for item in &items, если item ещё находится в области видимости?
Да. item содержит ссылку, но её заимствование действует только в пределах соответствующей итерации; после завершения цикла эти ссылки уничтожены. Вектор остаётся владельцем элементов и доступен после цикла, если внутри цикла не создано заимствование, выходящее за пределы цикла.
Что произойдёт, если тело цикла сохранит item при обходе по ссылке?
Сохранить сам &String можно только в структуре или контейнере с подходящим временем жизни: ссылка не может пережить вектор, которому она принадлежит. Если требуется независимое значение после уничтожения вектора, нужно явно скопировать данные, например item.clone(). Это меняет семантику с заимствования на создание нового владельца и несёт стоимость копирования.