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;
  3. // Define a structure which `fmt::Display` will be implemented for. This is simply
  4. // a tuple struct containing an `i32` bound to the name `Structure`.
  5. struct Structure(i32);
  6. // In order to use the `{}` marker, the trait `fmt::Display` must be implemented
  7. // manually for the type.
  8. impl fmt::Display for Structure {
  9. // This trait requires `fmt` with this exact signature.
  10. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  11. // Write strictly the first element into the supplied output
  12. // stream: `f`. Returns `fmt::Result` which indicates whether the
  13. // operation succeeded or failed. Note that `write!` uses syntax which
  14. // is very similar to `println!`.
  15. write!(f, "{}", self.0)
  16. }
  17. }

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`
  2. // A structure holding two numbers. `Debug` will be derived so the results can
  3. // be contrasted with `Display`.
  4. #[derive(Debug)]
  5. struct MinMax(i64, i64);
  6. // Implement `Display` for `MinMax`.
  7. impl fmt::Display for MinMax {
  8. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  9. // Use `self.number` to refer to each positional data point.
  10. write!(f, "({}, {})", self.0, self.1)
  11. }
  12. }
  13. // Define a structure where the fields are nameable for comparison.
  14. #[derive(Debug)]
  15. struct Point2D {
  16. x: f64,
  17. y: f64,
  18. }
  19. // Similarly, implement for Point2D
  20. impl fmt::Display for Point2D {
  21. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  22. // Customize so only `x` and `y` are denoted.
  23. write!(f, "x: {}, y: {}", self.x, self.y)
  24. }
  25. }
  26. fn main() {
  27. let minmax = MinMax(0, 14);
  28. println!("Compare structures:");
  29. println!("Display: {}", minmax);
  30. println!("Debug: {:?}", minmax);
  31. let big_range = MinMax(-300, 300);
  32. let small_range = MinMax(-3, 3);
  33. println!("The big range is {big} and the small is {small}",
  34. small = small_range,
  35. big = big_range);
  36. let point = Point2D { x: 3.3, y: 7.2 };
  37. println!("Compare points:");
  38. println!("Display: {}", point);
  39. println!("Debug: {:?}", point);
  40. // Error. Both `Debug` and `Display` were implemented but `{:b}`
  41. // requires `fmt::Binary` to be implemented. This will not work.
  42. // println!("What does Point2D look like in binary: {:b}?", point);
  43. }

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 }

See also

derive, std::fmt, macros, struct,
trait, and use