1.10 По частям
Брать с полки нужное, а не весь ряд - не умение, а очевидность. Очевидности имеют неприятную особенность: их понимают все, кроме инструментов. Список задач не исключение - посмотреть на него можно только целиком.
Попытка
add получила имя. Следующий шаг очевиден: дать имя ревизии. По аналогии с add -
&Vec<Task> на входе:
fn list(tasks: &Vec<Task>) {
for task in tasks {
println!("#{}: {} - {:?}", task.id, task.title, task.status);
}
}
Вызов с одной задачей работает:
add(&mut tasks, Task::new(1, "Buy coffee"));
list(&tasks);
Но нужно больше: показывать не весь список, а только часть. После добавления второй и третьей задачи - только их:
add(&mut tasks, Task::new(2, "Buy milk"));
add(&mut tasks, Task::new(3, "Buy eggs"));
list(&tasks[1..3]);
Компилятор подсказывает:
error[E0308]: mismatched types
--> src/main.rs:XX:XX
|
XX | list(&tasks[1..3]);
| ---- ^^^^^^^^^^^^ expected `&Vec<Task>`, found `&[Task]`
| |
| arguments to this function are incorrect
Что знает компилятор
&tasks[1..3] - не ссылка на Vec. Это &[Task]: ссылка на непрерывный участок с
задачами, начиная с индекса 1 и до 3 (не включая). Срез не знает, откуда данные - из
Vec, из массива, из другого среза.
&Vec<Task> и &[Task] - разные типы. Функция с параметром &Vec<Task> принимает только
ссылку на целый Vec - &[Task] сюда не подходит. Обратное устроено иначе: если функция
берёт &[Task], ей можно передать и &tasks (весь список), и &tasks[1..3] (часть).
Rust автоматически приводит &Vec<Task> к &[Task] при передаче - это встроенное правило
языка.
Синтаксис диапазонов:
| Запись | Что берёт |
|---|---|
&tasks[1..3] | с 1 до 3 (не включая) |
&tasks[1..] | с 1 до конца |
&tasks[..2] | с начала до 2 (не включая) |
&tasks[..] | весь список |
Заклинание
Одна правка - &Vec<Task> на &[Task]:
fn list(tasks: &[Task]) {
for task in tasks {
println!("#{}: {} - {:?}", task.id, task.title, task.status);
}
}
Теперь list принимает и целый список, и любую его часть. Два вызова в main:
fn main() {
let mut tasks: Vec<Task> = Vec::new();
add(&mut tasks, Task::new(1, "Buy coffee"));
list(&tasks);
add(&mut tasks, Task::new(2, "Buy milk"));
add(&mut tasks, Task::new(3, "Buy eggs"));
let task = &mut tasks[0];
task.complete();
if task.is_done() {
println!("Task 1 done");
}
list(&tasks[1..]);
}
#1: Buy coffee - Todo
Task 1 done
#2: Buy milk - Todo
#3: Buy eggs - Todo
make ci проходит. list принимает срез - и теперь умеет показывать любую часть списка.
Полный код
tqдля этой главы -1-a-task/10-the-list-in-parts/.
Lore: срез и целый список
list(&tasks) тоже работает - передать весь список без явного указания диапазона.
&tasks имеет тип &Vec<Task>, но при передаче в функцию с параметром &[Task] Rust
приводит его автоматически.
На практике это означает: функции, которые только читают список, лучше принимать &[Task].
Они становятся шире - работают с любым источником данных, а не только с Vec.