1.7 A Place to Keep Them
One piece needs no storage - it is simply there, in hand, on the workbench, nearby. A second piece requires a decision: where to put the first. A third requires a policy. A craftsman who can forge a piece but has not figured out where to put it starts from an empty table every time. Sometimes this is called a process. More often, a problem.
What You Need
Until now the work involved a single task, and everything was simple. But two tasks already
demand a naming system: task1, task2. With ten, that approach becomes awkward, and if
the number of tasks is unknown in advance - it becomes impossible.
A container is needed: somewhere to put tasks and retrieve them when the moment comes. In
Rust, a list of elements of the same type uses Vec. Vec<Task> is a list of tasks; the
angle brackets tell the compiler that only Task values go into this container. Angle
brackets will appear again in chapter 3.4 - for now, read Vec<Task> as “a list of tasks.”
Two operations matter with a container: put in, and take out.
The Build
Create the list and immediately try to add a task:
let tasks = Vec::new();
tasks.push(Task::new(1, "Buy coffee"));
The compiler, as usual, has something to say:
error[E0596]: cannot borrow `tasks` as mutable, as it is not declared as mutable
--> src/main.rs:XX:XX
|
XX | let tasks = Vec::new();
| ----- help: consider changing this to be mutable: `mut tasks`
XX | tasks.push(Task::new(1, "Buy coffee"));
| ^^^^^^^^^^^ cannot borrow as mutable
The same situation as with complete in chapter 1.6: modification requires mut. push
changes the list - it must be declared mutable:
let mut tasks: Vec<Task> = Vec::new();
Add the first task:
tasks.push(Task::new(1, "Buy coffee"));
Now try retrieving it into a variable and working with it as before:
let mut task = tasks[0];
The compiler reminds:
error[E0507]: cannot move out of index of `Vec<Task>`
|
| let mut task = tasks[0];
| ^^^^^^^^ move occurs because value has type `Task`,
| which does not implement the `Copy` trait
Moving a task out of the list is not allowed - it would leave a hole. tasks[0] is not a
separate variable but a position inside the Vec. To work with it, a reference is needed:
let task = &mut tasks[0];
&mut tasks[0] is a mutable reference to the first element inside the list. The task stays
in the Vec; task is just the way to reach it. The familiar API works through it:
println!("#{}: {}", task.id, task.title);
task.complete();
if task.is_done() {
println!("Task 1 done");
}
Put in and take out - confirmed. Add the rest:
tasks.push(Task::new(2, "Buy milk"));
tasks.push(Task::new(3, "Buy eggs"));
push appends to the end of the list - if you want to experiment, milk is at index 1
and eggs at 2.
The Result
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"));
}
#1: Buy coffee
Task 1 done
make ci passes. Vec stores the tasks; push adds them, &mut tasks[0] retrieves one
and allows working with it in place.
The complete
tqcode for this chapter is in1-a-task/07-a-place-to-keep-them/.
Scroll: Your data is not where you think it is Scroll: The borrow checker has one rule - and it is not the one you think