1.7 Полка
Одно изделие не требует хранения - оно просто есть, в руках, на верстаке, рядом. Второе изделие требует решения: куда девать первое. Третье - политику. Мастер, умеющий выковать изделие, но не придумавший, куда его класть, каждый раз начинает с пустого стола. Иногда это называют процессом. Чаще - проблемой.
Что нужно
До сих пор мы работали с одной задачей, всё было просто и понятно.
Но что если будет нужно две, тогда уже требуется система индексов: task1, task2.
С десятью этот подход становится неудобным, а если количество задач заранее неизвестно - просто невозможным!
Нужен контейнер: место, куда можно складывать и в нужный момент забирать задачи. В Rust
для списка элементов одного типа используют Vec. Vec<Task> - список задач; угловые скобки говорят
компилятору, что в этот контейнер войдут только Task. С угловыми скобками мы встретимся
снова в главе 3.4, а пока достаточно читать Vec<Task> как “список задач”.
С контейнером важно уметь делать две вещи: положить и взять.
Сборка
Создаём список и сразу пробуем положить задачу:
let tasks = Vec::new();
tasks.push(Task::new(1, "Buy coffee"));
Компилятор как обычно подсказывает:
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
Та же история, что и с complete в главе 1.6: изменение требует mut. push меняет список -
его нужно сделать изменяемым:
let mut tasks: Vec<Task> = Vec::new();
Добавим первую задачу:
tasks.push(Task::new(1, "Buy coffee"));
Теперь хочется её достать в переменную и работать так же, как и в прошлой главе:
let mut task = tasks[0];
Компилятор напоминает:
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
Переместить задачу из списка нельзя - в списке осталась бы дыра. tasks[0] - не отдельная
переменная, а позиция внутри Vec. Чтобы работать с ней, нужна ссылка:
let task = &mut tasks[0];
&mut tasks[0] - изменяемая ссылка на первый элемент прямо внутри списка. Задача остаётся
в Vec; task - это способ к ней обратиться. Весь знакомый API работает через неё:
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"));
push отправляет элементы в конец списка, если захочешь поэкспериментировать -
у milk индекс - 1, у eggs - 2.
Итог
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 проходит. Vec хранит задачи; push кладёт, &mut tasks[0] достаёт и позволяет
работать с элементом на месте.
Полный код
tqдля этой главы -1-a-task/07-a-place-to-keep-them/.
Свиток: Your data is not where you think it is Свиток: The borrow checker has one rule - and it is not the one you think