Display
fmt::Debug hardly looks compact and clean, so it is often advantageous to
customize the output appearance. This is done by manually implementing
fmt::Display, which uses the {} print marker. Implementing it
looks like this:
1. // Import (via `use`) the `fmt` module to make it available.
2. use std::fmt;
4. // Define a structure which `fmt::Display` will be implemented for. This is simply
5. // a tuple struct containing an `i32` bound to the name `Structure`.
6. struct Structure(i32);
8. // In order to use the `{}` marker, the trait `fmt::Display` must be implemented
9. // manually for the type.
10. impl fmt::Display for Structure {
11. // This trait requires `fmt` with this exact signature.
12. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
13. // Write strictly the first element into the supplied output
14. // stream: `f`. Returns `fmt::Result` which indicates whether the
15. // operation succeeded or failed. Note that `write!` uses syntax which
16. // is very similar to `println!`.
17. write!(f, "{}", self.0)
18. }
19. }
fmt::Display may be cleaner than fmt::Debug but this presents
a problem for the std library. How should ambiguous types be displayed?
For example, if the std library implemented a single style for all
Vec<T>, what style should it be? Either of these two?
Vec<path>:/:/etc:/home/username:/bin(split on:)Vec<number>:1,2,3(split on,)
No, because there is no ideal style for all types and the std library
doesn’t presume to dictate one. fmt::Display is not implemented for Vec<T>
or for any other generic containers. fmt::Debug must then be used for these
generic cases.
This is not a problem though because for any new container type which is
not generic,fmt::Display can be implemented.
1. use std::fmt; // Import `fmt`
3. // A structure holding two numbers. `Debug` will be derived so the results can
4. // be contrasted with `Display`.
5. #[derive(Debug)]
6. struct MinMax(i64, i64);
8. // Implement `Display` for `MinMax`.
9. impl fmt::Display for MinMax {
10. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
11. // Use `self.number` to refer to each positional data point.
12. write!(f, "({}, {})", self.0, self.1)
13. }
14. }
16. // Define a structure where the fields are nameable for comparison.
17. #[derive(Debug)]
18. struct Point2D {
19. x: f64,
20. y: f64,
21. }
23. // Similarly, implement for Point2D
24. impl fmt::Display for Point2D {
25. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26. // Customize so only `x` and `y` are denoted.
27. write!(f, "x: {}, y: {}", self.x, self.y)
28. }
29. }
31. fn main() {
32. let minmax = MinMax(0, 14);
34. println!("Compare structures:");
35. println!("Display: {}", minmax);
36. println!("Debug: {:?}", minmax);
38. let big_range = MinMax(-300, 300);
39. let small_range = MinMax(-3, 3);
41. println!("The big range is {big} and the small is {small}",
42. small = small_range,
43. big = big_range);
45. let point = Point2D { x: 3.3, y: 7.2 };
47. println!("Compare points:");
48. println!("Display: {}", point);
49. println!("Debug: {:?}", point);
51. // Error. Both `Debug` and `Display` were implemented but `{:b}`
52. // requires `fmt::Binary` to be implemented. This will not work.
53. // println!("What does Point2D look like in binary: {:b}?", point);
54. }
So, fmt::Display has been implemented but fmt::Binary has not, and
therefore cannot be used. std::fmt has many such traits and
each requires its own implementation. This is detailed further in
std::fmt.
Activity
After checking the output of the above example, use the Point2D struct as
guide to add a Complex struct to the example. When printed in the same
way, the output should be:
1. Display: 3.3 +7.2i
2. Debug: Complex { real: 3.3, imag: 7.2 }
