← Оглавление

1.3 Речь

Попросить вещь описать себя - разумное желание, особенно когда что-то идёт не так. Задача в коде на такую просьбу не ответит: форма есть, данные есть, но голоса нет. Молчание - не отказ. Молчание - это когда вопрос даже не был услышан.

Попытка

Поля сейчас печатаются по одному. Когда задач станет больше - и полей, и задач - это быстро станет громоздким. Разумнее попросить задачу описать себя целиком. В Rust для этого есть специальный форматный флаг - {:?}:

println!("{:?}", task);

Компилятор подсказывает:

error[E0277]: `Task` doesn't implement `Debug`
  --> src/main.rs:XX:XX
   |
XX |     println!("{:?}", task);
   |                      ^^^^ `Task` cannot be formatted using `{:?}`
   |
   = help: the trait `Debug` is not implemented for `Task`
   = note: add `#[derive(Debug)]` to `Task` or manually `impl Debug for Task`

Что знает компилятор

{:?} - это запрос: “опиши себя в отладочном формате”. Чтобы выполнить его, тип должен уметь это делать. Компилятор не может придумать описание за тип - он требует явного обязательства.

В Rust такие обязательства называются трейтами. Трейт - это набор возможностей, которые тип обязуется предоставить. Debug - один из них: тип, реализующий Debug, умеет описывать себя для отладки. Task такого обязательства пока не давал.

Трейты в Rust делают многое - глава 3.1 разберёт их подробнее. Сейчас достаточно одного: без Debug компилятор не знает, как обрабатывать {:?}.

Заклинание

Компилятор в сообщении об ошибке уже подсказал решение: add #[derive(Debug)] to Task.

#[derive(Debug)]
struct Task {
    id: u64,
    title: String,
    status: bool,
}

#[derive(Debug)] - атрибут, который просит компилятор сгенерировать реализацию Debug автоматически. Это возможно, потому что все поля Task - u64, String, bool - уже умеют описывать себя. Компилятор собирает их описания вместе.

Теперь {:?} работает:

println!("{:?}", task);
Task { id: 1, title: "Buy coffee", status: false }

Есть ещё {:#?} - тот же формат, но с переносами строк и отступами:

println!("{:#?}", task);
Task {
    id: 1,
    title: "Buy coffee",
    status: false,
}

Задача умеет описывать себя. Когда что-то пойдёт не так - будет что распечатать.

Полный код tq для этой главы - 1-a-task/03-teaching-things-to-speak/.


Lore: Debug и Display

Debug - не единственный трейт для вывода. В Rust их два:

println!("{}", task) сейчас всё ещё не сработает: Display у Task нет. Он появится позже - когда задача научится говорить на языке пользователя, а не только разработчика.