Введение
В этом практическом занятии вы научитесь реализовывать трейт fmt::Display в Rust для настройки внешнего вида вывода структуры. Также вы изучите разницу между fmt::Display и fmt::Debug, а также ограничения fmt::Display для обобщенных типов контейнеров. Наконец, у вас будет задание по реализации трейта fmt::Display для новой структуры Complex и выводу ее в определенном формате.
Примечание: Если практическое занятие не задает имя файла, вы можете использовать любое имя, которое хотите. Например, вы можете использовать
main.rs, скомпилировать и запустить его с помощьюrustc main.rs &&./main.
Display
fmt::Debug редко выглядит компактным и чистым, поэтому часто выгодно настроить внешний вид вывода. Это делается путём ручного реализации fmt::Display, который использует маркер вывода {}. Реализация выглядит так:
// Импортируем (через `use`) модуль `fmt`, чтобы иметь возможность использовать его.
use std::fmt;
// Определяем структуру, для которой будет реализован `fmt::Display`. Это
// кортежная структура под названием `Structure`, которая содержит `i32`.
struct Structure(i32);
// Чтобы использовать маркер `{}`, для типа необходимо вручную реализовать
// трейт `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. Как следует выводить неоднозначные типы? Например, если стандартная библиотека реализовала единый стиль для всех Vec<T>, какой стиль должен быть выбран? Будет ли это один из двух вариантов?
Vec<путь>:/:/etc:/home/username:/bin(разделено по:)Vec<число>:1,2,3(разделено по,)
Нет, потому что для всех типов нет идеального стиля, и стандартная библиотека не настаивает на одном стиле. 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` для `Point2D`.
impl fmt::Display for Point2D {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Настраиваем вывод так, чтобы обозначались только `x` и `y`.
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 }
Резюме
Поздравляем! Вы завершили практическое занятие по Display. Вы можете выполнить больше практических заданий в LabEx, чтобы улучшить свои навыки.