fmt::Debug
выглядит не очень компактно и красиво, поэтому полезно настраивать внешний вид информации, которая будет напечатана. Это можно сделать реализовав типаж (trait)
fmt::Display
вручную, который использует маркер {}
для печати. Его реализация выглядит следующим образом:
// Импортируем (с помощью `use`) модуль `fmt`, чтобы мы могли его использовать.
use std::fmt;
// Определяем структуру, для которой будет реализован `fmt::Display`.
// Это простая кортежная структура, которая хранит в себе `i32` и которой присвоено имя `Structure`.
struct Structure(i32);
// Чтобы была возможность использовать маркер `{}`
// `типаж (trait) fmt::Display` должен быть реализован вручную
// для данного типа.
impl fmt::Display for Structure {
// Этот типаж требует реализацию метода `fmt` с данной сигнатурой:
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Записываем первый элемент в предоставленный выходной поток: `f`.
// Возвращаем `fmt::Result`, который показывает выполнилась операция
// успешно или нет. Обратите внимание на то, что синтаксис `write!`
// похож на синтаксис `println!`.
write!(f, "{}", self.0)
}
}
Вывод fmt::Display
может быть более чистым чем fmt::Debug
, но может быть
проблемой для стандартной библиотеки (std)
. Как нестандартные типы должны отображаться?
Например, если стандартная библиотека (std)
предоставляет единый стиль вывода для
Vec<T>
, каким этот вывод должен быть? Любой из этих двух?
Vec<path>
: /:/etc:/home/username:/bin
(разделитель :
)Vec<number>
: 1,2,3
(разделитель ,
)Нет, потому что не существует идеального стиля вывода для всех типов, поэтому
стандартная библиотека std
не может его предоставить. fmt::Display
не реализован для
Vec<T>
или для других обобщенных контейнеров. Для этих случаев подойдет fmt::Debug
.
Это не проблема, потому что для любых новых контейнеров, типы которых не обобщенные, может быть реализован fmt::Display
.
use std::fmt; // Импортируем `fmt` // Структура, которая хранит в себе два числа. //Вывод типажа `Debug` добавлен для сравнения с `Display`. #[derive(Debug)] struct MinMax(i64, i64); // Реализуем `Display` для `MinMax`. impl fmt::Display for MinMax { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Используем `self.number`, чтобы получить доступ к каждому полю структуры. write!(f, "({}, {})", self.0, self.1) } } // Определим структуру с именованными полями, для сравнения #[derive(Debug)] struct Point2D { x: f64, y: f64, } // По аналогии, реализуем `Display` для Point2 impl fmt::Display for Point2D { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Обращаться к полям структуры Point2 будет по имени write!(f, "x: {}, y: {}", self.x, self.y) } } fn main() { let minmax = MinMax(0, 14); println!("Compare structures:"); println!("Display: {}", minmax); println!("Debug: {:?}", minmax); let big_range = MinMax(-300, 300); let small_range = MinMax(-3, 3); println!("The big range is {big} and the small is {small}", small = small_range, big = big_range); let point = Point2D { x: 3.3, y: 7.2 }; println!("Compare points:"); println!("Display: {}", point); println!("Debug: {:?}", point); // Ошибка. Типажи `Debug` и `Display` были реализованны, но `{:b}` // необходима реализация `fmt::Binary`. Следующий код не сработает. // println!("What does Point2D look like in binary: {:b}?", point); }
И так, fmt::Display
был реализован, но fmt::Binary
нет, следовательно не может быть
использован. std::fmt
имеет много таких типажей
и
каждый из них требует свою реализацию. Это более подробно описано в документации к
std::fmt
.
После того, как запустите код, представленный выше, используйте структуру Point2D
как пример и добавьте новую структуру Complex
, чтобы вывод был таким:
Display: 3.3 + 7.2i
Debug: Complex { real: 3.3, imag: 7.2 }