| # 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`][fmt], which uses the `{}` print marker. Implementing it |
| looks like this: |
| |
| ```rust |
| // 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` 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? Would 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. |
| |
| ```rust,editable |
| 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); |
| |
| // Error. Both `Debug` and `Display` were implemented, but `{:b}` |
| // requires `fmt::Binary` to be implemented. This will not work. |
| // println!("What does Point2D look like in binary: {:b}?", point); |
| } |
| ``` |
| |
| So, `fmt::Display` has been implemented but `fmt::Binary` has not, and therefore |
| cannot be used. `std::fmt` has many such [`traits`][traits] and each requires |
| its own implementation. This is detailed further in [`std::fmt`][fmt]. |
| |
| ### Activity |
| |
| After checking the output of the above example, use the `Point2D` struct as a |
| guide to add a `Complex` struct to the example. When printed in the same |
| way, the output should be: |
| |
| ```txt |
| 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 } |
| ``` |
| |
| Bonus: Add a space before the `+`/`-` signs. |
| |
| Hints in case you get stuck: |
| |
| - Check the documentation for [`Sign/#/0`][fmt_sign0] in `std::fmt`. |
| - Bonus: Check [`if`-`else`][if_else] branching and the [`abs`][f64_abs] function. |
| |
| ### See also: |
| |
| [`derive`][derive], [`std::fmt`][fmt], [`macros`][macros], [`struct`][structs], |
| [`trait`][traits], and [`use`][use] |
| |
| [derive]: ../../trait/derive.md |
| [f64_abs]: https://doc.rust-lang.org/std/primitive.f64.html#method.abs |
| [fmt]: https://doc.rust-lang.org/std/fmt/ |
| [fmt_sign0]: https://doc.rust-lang.org/std/fmt/#sign0 |
| [if_else]: ../../flow_control/if_else.md |
| [macros]: ../../macros.md |
| [structs]: ../../custom_types/structs.md |
| [traits]: https://doc.rust-lang.org/std/fmt/#formatting-traits |
| [use]: ../../mod/use.md |