← Оглавление

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.