1.3 Teaching Things to Speak
Asking a thing to describe itself is a reasonable wish, especially when something goes wrong. A task in the code will not answer such a request: the shape exists, the data exists, but there is no voice. Silence is not a refusal. Silence is when the question was never heard at all.
The Attempt
The fields are currently printed one by one. As the number of fields and tasks grows, that
will become unwieldy quickly. It is more sensible to ask the task to describe itself as a
whole. Rust has a special format flag for this - {:?}:
println!("{:?}", task);
The compiler responds:
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`
What the Compiler Knows
{:?} is a request: “describe yourself in debug format.” For a type to fulfil that request,
it must know how. The compiler cannot invent a description - it requires an explicit
commitment.
In Rust, these commitments are called traits. A trait is a set of capabilities a type
promises to provide. Debug is one of them: a type that implements Debug knows how to
describe itself for debugging purposes. Task has made no such commitment yet.
Traits do a great deal in Rust - chapter 3.1 covers them in depth. For now, one thing is
enough: without Debug, the compiler does not know how to handle {:?}.
The Enchantment
The compiler already gave the answer in the error message: add #[derive(Debug)] to Task.
#[derive(Debug)]
struct Task {
id: u64,
title: String,
status: bool,
}
#[derive(Debug)] is an attribute that asks the compiler to generate a Debug implementation
automatically. This works because all of Task’s fields - u64, String, bool - already
know how to describe themselves. The compiler assembles their descriptions together.
Now {:?} works:
println!("{:?}", task);
Task { id: 1, title: "Buy coffee", status: false }
There is also {:#?} - the same format, but with line breaks and indentation:
println!("{:#?}", task);
Task {
id: 1,
title: "Buy coffee",
status: false,
}
The task can describe itself. When something goes wrong, there will be something to print.
The complete
tqcode for this chapter is in1-a-task/03-teaching-things-to-speak/.
Lore: Debug and Display
Debug is not the only trait for output. Rust has two:
Debug- the developer’s format. Activated via{:?}or{:#?}. Shows the internal structure of the type. Generated automatically viaderive.Display- the user’s format. Activated via{}. Shows what the reader of the program should see. Not generated automatically - it must be written by hand.
println!("{}", task) still will not work: Task has no Display. It will appear later -
when the task learns to speak in the user’s language, not just the developer’s.