Rust: с нуля до своего органайзера Урок 41 из 50
Итераторы: читать, менять или передавать владение
Урок 41. Выбрать способ обхода задач, не потеряв данные раньше времени.
Текст переведён с помощью ИИ.
Предварительно: урок 20 о заимствовании, урок 24 о Vec, урок 40 об органайзере. Исходное состояние — копия проекта из урока 40; сохраните её отдельно. Здесь ради одного нового понятия main.rs заменяется короткой программой, данные из прежнего запуска не переносятся.
Образ и опорная карта
У библиотекаря есть три способа пройти вдоль полки: посмотреть на корешки, исправить карточки, вынести книги. Итератор — объект, который выдаёт элементы по одному и помнит место обхода. Аналогия кончается там, где начинается владение Rust: iter() даёт общие ссылки &Task, iter_mut() — изменяемые &mut Task, into_iter() забирает элементы из списка. После последнего способа прежний Vec использовать нельзя. Ссылки и владение разобраны в уроках 19–21.
Список → iter (читать) / iter_mut (менять) / into_iter (передать) → следующий элемент либо конец. next() делает один шаг: возвращает Some с элементом или None в конце. let mut reader нужен потому, что каждый шаг меняет положение итератора. Создание итератора само по себе не проходит список — он ленив. Цикл for запрашивает элементы до конца. Три прохода в примере не существуют одновременно: изменяемый доступ начинается после завершения чтения. if let Some(task) выполняет тело только при найденном элементе. !task.done означает «ещё не готово».
Запустите и наблюдайте
В папке проекта замените src/main.rs кодом ниже и выполните знакомую по уроку 6 команду cargo run. Дополнительных библиотек нет. Сообщения Cargo не входят в показанный вывод программы. На узком экране длинную строку кода можно прокрутить пальцем внутри светлого блока; сама страница остаётся на месте.
struct Task {
id: u32,
title: String,
done: bool,
}
fn main() {
let mut tasks = vec![
Task {
id: 1,
title: String::from("Читать Rust"),
done: false,
},
Task {
id: 2,
title: String::from("Записать идею"),
done: false,
},
];
let mut reader = tasks.iter();
if let Some(task) = reader.next() {
println!("Первая: {}", task.title);
}
for task in tasks.iter_mut() {
if task.id == 2 {
task.done = true;
}
}
for task in tasks.iter() {
println!("Задача {}: {}", task.id, task.title);
}
let mut done_count = 0;
for task in tasks.iter() {
if task.done {
done_count += 1;
}
}
println!("Готово: {}", done_count);
for task in tasks.into_iter() {
println!("Владеем: {}", task.title);
}
}
Первая: Читать Rust
Задача 1: Читать Rust
Задача 2: Записать идею
Готово: 1
Владеем: Читать Rust
Владеем: Записать идею
Сначала читается первая задача, затем меняется отметка задачи 2. Последний цикл получает названия во владение и завершает использование списка. Ошибка новичка — написать tasks.len() после into_iter(): значение уже передано; компилятор это остановит. Для простой печати обычно достаточно iter(). Всё ещё хранится только в памяти одного запуска.
Вспомните без подсказки
- Какой из трёх способов нужен, чтобы отметить задачу готовой?
- Почему после
into_iter()нельзя снова читатьtasks? - Пройдёт ли список одно лишь
let reader = tasks.iter()?
Задание
Обязательное. До цикла into_iter() посчитайте отдельным проходом, сколько задач ещё ожидают выполнения, и выведите Ожидают: 1. Сохраните уже показанный порядок строк. Затем объясните, почему счётчик не требует передачи владения.
Ответы
Показать ответы
Заведите pending_count = 0, обойдите tasks.iter() и увеличьте счётчик при !task.done. Делайте это до into_iter().
Эталон после попытки
struct Task {
id: u32,
title: String,
done: bool,
}
fn main() {
let mut tasks = vec![
Task {
id: 1,
title: String::from("Читать Rust"),
done: false,
},
Task {
id: 2,
title: String::from("Записать идею"),
done: false,
},
];
let mut reader = tasks.iter();
if let Some(task) = reader.next() {
println!("Первая: {}", task.title);
}
for task in tasks.iter_mut() {
if task.id == 2 {
task.done = true;
}
}
for task in tasks.iter() {
println!("Задача {}: {}", task.id, task.title);
}
let mut done_count = 0;
for task in tasks.iter() {
if task.done {
done_count += 1;
}
}
println!("Готово: {}", done_count);
let mut pending_count = 0;
for task in tasks.iter() {
if !task.done {
pending_count += 1;
}
}
println!("Ожидают: {}", pending_count);
for task in tasks.into_iter() {
println!("Владеем: {}", task.title);
}
}
Первая: Читать Rust
Задача 1: Читать Rust
Задача 2: Записать идею
Готово: 1
Ожидают: 1
Владеем: Читать Rust
Владеем: Записать идею
После проверки
Проверка: счётчик добавлен до передачи владения, исходные ID и отметки не меняются. Официальная глава об итераторах. Если путаются ссылки, вернитесь к уроку 20.
Если вы нашли ошибку или опечатку в тексте статьи, то сообщите нам об этом
Комментарии (0)
Войдите, чтобы оставить комментарий →
Пока нет комментариев. Будьте первым.