Трейт Debug позволяет вывести данные на консоль в удобном для разработчиков виде. Что довольно удобно для отладки. Для применения трейта Debug
при выводе на консоль, например, с помощью макроса println! внутри фигурных указывается спецификатор :?. Например:
fn main() {
let tom = ("Tom", 40);
println!("{:?}", tom); // ("Tom", 40)
}
В данном случае спецификатор :? применяется для вывода на консоль кортежа с двумя элементами. И, таким образом, мы сможем увидеть весь кортеж.
Однако не все типы мы можем так вывести на консоль. Например, что, если у нас есть структура со множеством полей, и мы хотим вывести структуру в удобочитаемом виде без необходимости выводить каждое поле по отдельности:
struct Person
{
name: String,
age: u8
}
fn main() {
let tom = Person{name:"Tom".to_string(), age: 40};
println!("{:?}", tom); // ! Ошибка - Person cannot be formatted using `{:?}`
}
Здесь при попытке вывести значение структуры Person мы столкнемся с ошибкой, поскольку эта структура не реализует трейт Debug.
К счастью, реализовать этот трейт не так сложно. Для этого Rust предоставляет атрибут #[derive(Debug)], который надо применить к структуре:
#[derive(Debug)]
struct Person
{
name: String,
age: u8
}
fn main() {
let tom = Person{name:"Tom".to_string(), age: 40};
println!("{:?}", tom); // Person { name: "Tom", age: 40 }
}
И консоль нам выведет
Person { name: "Tom", age: 40 }
И мы можем пойти дальше и вывести данные в более удобочитаемом виде, применив спецификатор :#?
println!("{:#?}", tom);
Консольный вывод:
Person {
name: "Tom",
age: 40,
}
Стоит отметить, что есть еще один способ вывести значение в формате отладки — использовать макрос dbg!. При этом данный макрос также
выводит файл и номер строки, где находится текущий вызов макроса dbg!. Однако стоит учитывать, что этот макрос получает владение над передаваемым
значением, поэтому лучше в этот макрос передать ссылку на значение:
#[derive(Debug)]
struct Person
{
name: String,
age: u8
}
fn main() {
let tom = Person{name:"Tom".to_string(), age: 40};
dbg!(&tom);
}
Консольный вывод:
[main.rs:10:5] &tom = Person {
name: "Tom",
age: 40,
}
Реализация трейта fmt::Display также позволяет выводить нам на консоль некоторое значение, только с помощью плейсхолдера "{}". Многие встроенные типы реализуют этот трейт. Посмотрим, как его реализовать для кастомных типов:
use std::fmt;
struct Person
{
name: String,
age: u8
}
impl fmt::Display for Person {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Person {} (age: {})", self.name, self.age)
}
}
fn main() {
let tom = Person{name:"Tom".to_string(), age: 40};
println!("{}", tom); // Person Tom (age: 40)
}
Здесь трейт fmt::Display также реализуется для структуры Person. Реализация трейта представляет реализацию функции fmt(), которая принимает
два параметра - ссылку на текущий объект структуры - &self и форматировщик fmt::Formatter. Функция возвращает значение типа fmt::Result.
И наша реализация довольно проста - с помощью макроса write! определяем, как данные структуры Person будут выводиться на консоль.