| # Formatting |
| |
| We've seen that formatting is specified via a *format string*: |
| |
| * `format!("{}", foo)` -> `"3735928559"` |
| * `format!("0x{:X}", foo)` -> [`"0xDEADBEEF"`][deadbeef] |
| * `format!("0o{:o}", foo)` -> `"0o33653337357"` |
| |
| The same variable (`foo`) can be formatted differently depending on which |
| *argument type* is used: `X` vs `o` vs *unspecified*. |
| |
| This formatting functionality is implemented via traits, and there is one trait |
| for each argument type. The most common formatting trait is `Display`, which |
| handles cases where the argument type is left unspecified: `{}` for instance. |
| |
| ```rust,editable |
| use std::fmt::{self, Formatter, Display}; |
| |
| struct City { |
| name: &'static str, |
| // Latitude |
| lat: f32, |
| // Longitude |
| lon: f32, |
| } |
| |
| impl Display for City { |
| // `f` is a buffer, and this method must write the formatted string into it. |
| fn fmt(&self, f: &mut Formatter) -> fmt::Result { |
| let lat_c = if self.lat >= 0.0 { 'N' } else { 'S' }; |
| let lon_c = if self.lon >= 0.0 { 'E' } else { 'W' }; |
| |
| // `write!` is like `format!`, but it will write the formatted string |
| // into a buffer (the first argument). |
| write!(f, "{}: {:.3}°{} {:.3}°{}", |
| self.name, self.lat.abs(), lat_c, self.lon.abs(), lon_c) |
| } |
| } |
| |
| #[derive(Debug)] |
| struct Color { |
| red: u8, |
| green: u8, |
| blue: u8, |
| } |
| |
| fn main() { |
| for city in [ |
| City { name: "Dublin", lat: 53.347778, lon: -6.259722 }, |
| City { name: "Oslo", lat: 59.95, lon: 10.75 }, |
| City { name: "Vancouver", lat: 49.25, lon: -123.1 }, |
| ] { |
| println!("{}", city); |
| } |
| for color in [ |
| Color { red: 128, green: 255, blue: 90 }, |
| Color { red: 0, green: 3, blue: 254 }, |
| Color { red: 0, green: 0, blue: 0 }, |
| ] { |
| // Switch this to use {} once you've added an implementation |
| // for fmt::Display. |
| println!("{:?}", color); |
| } |
| } |
| ``` |
| |
| You can view a [full list of formatting traits][fmt_traits] and their argument |
| types in the [`std::fmt`][fmt] documentation. |
| |
| ### Activity |
| |
| Add an implementation of the `fmt::Display` trait for the `Color` struct above |
| so that the output displays as: |
| |
| ```text |
| RGB (128, 255, 90) 0x80FF5A |
| RGB (0, 3, 254) 0x0003FE |
| RGB (0, 0, 0) 0x000000 |
| ``` |
| |
| Two hints if you get stuck: |
| |
| * You [may need to list each color more than once][named_parameters]. |
| * You can [pad with zeros to a width of 2][fmt_width] with `:0>2`. |
| For hexadecimals, you can use `:02X`. |
| |
| Bonus: |
| |
| * If you would like to experiment with [type casting][type_casting] in advance, |
| the formula for [calculating a color in the RGB color space][rgb_color] is |
| `RGB = (R * 65_536) + (G * 256) + B`, where `R is RED, G is GREEN, and B is BLUE`. |
| An unsigned 8-bit integer (`u8`) can only hold numbers up to 255. To cast `u8` to `u32`, you can write `variable_name as u32`. |
| |
| ### See also: |
| |
| [`std::fmt`][fmt] |
| |
| [rgb_color]: https://www.rapidtables.com/web/color/RGB_Color.html#rgb-format |
| [named_parameters]: https://doc.rust-lang.org/std/fmt/#named-parameters |
| [deadbeef]: https://en.wikipedia.org/wiki/Deadbeef#Magic_debug_values |
| [fmt]: https://doc.rust-lang.org/std/fmt/ |
| [fmt_traits]: https://doc.rust-lang.org/std/fmt/#formatting-traits |
| [fmt_width]: https://doc.rust-lang.org/std/fmt/#width |
| [type_casting]: ../../types/cast.md |