← Оглавление

1.8 Ревизия

Теоретически, если точно знать количество задач в списке и не забывать, что счёт начинается с нуля, цикл не нужен. Практика показывает, что оба условия нарушаются быстрее, чем ожидается. Особенно второе.

Попытка

Итак, у нас уже сформирован список задач. Хочется пройтись по нему и посмотреть на всё сразу прежде чем добавлять новое. В Rust для этого есть удобная конструкция:

for task in tasks {
    println!("#{}: {} - {:?}", task.id, task.title, task.status);
}
tasks.push(Task::new(2, "Buy milk"));

for ... in - цикл: берёт элементы коллекции по очереди, для каждого выполняет тело (между фигурными скобками). Выглядит хорошо. Но у компилятора есть замечание:

error[E0382]: borrow of moved value: `tasks`
   |
   |     let mut tasks: Vec<Task> = Vec::new();
   |         --------- move occurs because `tasks` has type `Vec<Task>`,
   |                   which does not implement the `Copy` trait
   |     for task in tasks {
   |                 ----- `tasks` moved due to this implicit call to `.into_iter()`
   |     tasks.push(Task::new(2, "Buy milk"));
   |     ^^^^^ value borrowed here after move

Что знает компилятор

for task in tasks - это перемещение. Цикл забирает tasks целиком и становится его владельцем. Когда цикл заканчивается, коллекция уничтожается вместе с ним. Строка с push обращается к тому, чего уже нет.

Мы видели аналогичное в главе 1.5: передать - значит отдать. Только здесь получатель не функция, а цикл.

Решение то же: передать право на чтение, а не владение:

for task in &tasks {
    println!("{:?}", task);
}

&tasks - ссылка на коллекцию. Цикл читает через неё; tasks остаётся жить в main.

Заклинание

fn main() {
    let mut tasks: Vec<Task> = Vec::new();
    tasks.push(Task::new(1, "Buy coffee"));

    let task = &mut tasks[0];
    println!("#{}: {}", task.id, task.title);
    task.complete();
    if task.is_done() {
        println!("Task 1 done");
    }

    tasks.push(Task::new(2, "Buy milk"));
    tasks.push(Task::new(3, "Buy eggs"));

    for task in &tasks {
        println!("{:?}", task);
    }
}
#1: Buy coffee
Task 1 done
Task { id: 1, title: "Buy coffee", status: Done }
Task { id: 2, title: "Buy milk", status: Todo }
Task { id: 3, title: "Buy eggs", status: Todo }

make ci проходит. А мы знаем как сделать ревизию всей коллекции.

Полный код tq для этой главы - 1-a-task/08-the-inventory/.