Display
fmt::Debug навряд чи виглядає компактним і чистим, тому часто вигідно
налаштувати вигляд виводу. Це робиться шляхом ручного виведення
реалізації fmt::Display, який використовує позначку друку {}. Реалізація цього
виглядає так:
#![allow(unused)]
fn main() {
// Import (via `use`) the `fmt` module to make it available.
use std::fmt;
// Define a structure for which `fmt::Display` will be implemented. This is
// a tuple struct named `Structure` that contains an `i32`.
struct Structure(i32);
// To use the `{}` marker, the trait `fmt::Display` must be implemented
// manually for the type.
impl fmt::Display for Structure {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Write strictly the first element into the supplied output
// stream: `f`. Returns `fmt::Result` which indicates whether the
// operation succeeded or failed. Note that `write!` uses syntax which
// is very similar to `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; // Import `fmt`
// A structure holding two numbers. `Debug` will be derived so the results can
// be contrasted with `Display`.
#[derive(Debug)]
struct MinMax(i64, i64);
// Implement `Display` for `MinMax`.
impl fmt::Display for MinMax {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Use `self.number` to refer to each positional data point.
write!(f, "({}, {})", self.0, self.1)
}
}
// Define a structure where the fields are nameable for comparison.
#[derive(Debug)]
struct Point2D {
x: f64,
y: f64,
}
// Similarly, implement `Display` for `Point2D`.
impl fmt::Display for Point2D {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Customize so only `x` and `y` are denoted.
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);
// The following line would not compile: both `Debug` and `Display`
// were implemented, but `{:b}` requires `fmt::Binary` to be
// implemented, which it hasn't been for `Point2D`.
// println!("What does Point2D look like in binary: {:b}?", point);
}
Отже, fmt::Display було реалізовано, але fmt::Binary — ні, і тому
його не можна використовувати. std::fmt має багато таких traits, і кожен вимагає
своєї реалізації. Це детальніше описано в std::fmt.
Activity
Після перевірки виводу наведеного вище прикладу, використайте структуру
Point2D як зразок, щоб додати до прикладу структуру Complex. Коли її буде надруковано тим самим
способом, вивід має бути таким:
Display: 3.3 +7.2i
Debug: Complex { real: 3.3, imag: 7.2 }
Display: 4.7 -2.3i
Debug: Complex { real: 4.7, imag: -2.3 }
Бонус: Додайте пробіл після знаків +/-.
Підказки на випадок, якщо ви застрягнете:
- Перевірте документацію для
Sign/#/0уstd::fmt. - Бонус: Перевірте розгалуження
if-elseі функціюabs.