Display
fmt::Debug
выглядит не очень компактно и красиво, поэтому полезно настраивать внешний вид информации, которая будет напечатана. Это можно сделать реализовав типаж fmt::Display
вручную, который использует маркер {}
для печати. Его реализация выглядит следующим образом:
#![allow(unused)] fn main() { // Импортируем (с помощью `use`) модуль `fmt`, чтобы мы могли его использовать. use std::fmt; // Определяем структуру, для которой будет реализован `fmt::Display`. // Это простая кортежная структура c именем `Structure`, которая хранит в себе `i32`. 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.номер`, чтобы получить доступ к каждому полю структуры. write!(f, "({}, {})", self.0, self.1) } } // Объявим структуру с именованными полями, для сравнения #[derive(Debug)] struct Point2D { x: f64, y: f64, } // По аналогии, реализуем `Display` для Point2D impl fmt::Display for Point2D { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Обращаться к полям структуры Point2D будет по имени write!(f, "x: {}, y: {}", self.x, self.y) } } fn main() { let minmax = MinMax(0, 14); println!("Сравниваем структуры:"); println!("Display: {}", minmax); println!("Debug: {:?}", minmax); let big_range = MinMax(-300, 300); let small_range = MinMax(-3, 3); println!("Большой диапазон - {big} и маленький диапазон {small}", small = small_range, big = big_range); let point = Point2D { x: 3.3, y: 7.2 }; println!("Сравниваем точки:"); println!("Display: {}", point); println!("Debug: {:?}", point); // Ошибка. Типажи `Debug` и `Display` были реализованы, но `{:b}` // необходима реализация `fmt::Binary`. Следующий код не сработает. // println!("Как выглядит Point2D в виде двоичного кода: {: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 }