← Table of contents

1.8 The Inventory

In theory, if you know the exact number of tasks in the list and remember that counting starts at zero, a loop is unnecessary. In practice, both conditions break down faster than expected. Especially the second.

The Attempt

The list of tasks is already formed. The natural next step is to walk through it and see everything at once before adding anything new. Rust has a convenient construction for this:

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

for ... in is a loop: it takes elements from a collection one by one and runs the body (inside the curly braces) for each. Looks right. But the compiler has a note:

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

What the Compiler Knows

for task in tasks is a move. The loop takes tasks entirely and becomes its owner. When the loop ends, the collection is destroyed along with it. The line with push reaches for something that no longer exists.

The same pattern appeared in chapter 1.5: to pass is to give away. Here the recipient is not a function but a loop.

The fix is the same: pass the right to read, not ownership:

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

&tasks is a reference to the collection. The loop reads through it; tasks keeps living in main.

The Enchantment

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 passes. The full collection can now be reviewed in one pass.

The complete tq code for this chapter is in 1-a-task/08-the-inventory/.