Rust Formatting and Display Trait

RustRustBeginner
Practice Now

This tutorial is from open-source community. Access the source code

Introduction

In this lab, we learned about formatting in Rust and how to use the format! macro to format variables. We saw that formatting is specified using a format string, and different argument types can be used to format the same variable in different ways. The most common formatting trait is Display, which handles cases where the argument type is left unspecified. We saw an example of implementing the Display trait for a City struct, where we formatted the latitude and longitude values. We also saw an example of a Color struct and were tasked with implementing the Display trait for it to display the RGB values and their hexadecimal representation.

Note: If the lab does not specify a file name, you can use any file name you want. For example, you can use main.rs, compile and run it with rustc main.rs && ./main.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("`Rust`")) -.-> rust/BasicConceptsGroup(["`Basic Concepts`"]) rust(("`Rust`")) -.-> rust/DataTypesGroup(["`Data Types`"]) rust(("`Rust`")) -.-> rust/ControlStructuresGroup(["`Control Structures`"]) rust(("`Rust`")) -.-> rust/FunctionsandClosuresGroup(["`Functions and Closures`"]) rust(("`Rust`")) -.-> rust/MemorySafetyandManagementGroup(["`Memory Safety and Management`"]) rust(("`Rust`")) -.-> rust/DataStructuresandEnumsGroup(["`Data Structures and Enums`"]) rust(("`Rust`")) -.-> rust/AdvancedTopicsGroup(["`Advanced Topics`"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("`Variable Declarations`") rust/DataTypesGroup -.-> rust/integer_types("`Integer Types`") rust/DataTypesGroup -.-> rust/floating_types("`Floating-point Types`") rust/DataTypesGroup -.-> rust/string_type("`String Type`") rust/ControlStructuresGroup -.-> rust/for_loop("`for Loop`") rust/FunctionsandClosuresGroup -.-> rust/function_syntax("`Function Syntax`") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("`Expressions and Statements`") rust/MemorySafetyandManagementGroup -.-> rust/lifetime_specifiers("`Lifetime Specifiers`") rust/DataStructuresandEnumsGroup -.-> rust/method_syntax("`Method Syntax`") rust/AdvancedTopicsGroup -.-> rust/traits("`Traits`") subgraph Lab Skills rust/variable_declarations -.-> lab-99190{{"`Rust Formatting and Display Trait`"}} rust/integer_types -.-> lab-99190{{"`Rust Formatting and Display Trait`"}} rust/floating_types -.-> lab-99190{{"`Rust Formatting and Display Trait`"}} rust/string_type -.-> lab-99190{{"`Rust Formatting and Display Trait`"}} rust/for_loop -.-> lab-99190{{"`Rust Formatting and Display Trait`"}} rust/function_syntax -.-> lab-99190{{"`Rust Formatting and Display Trait`"}} rust/expressions_statements -.-> lab-99190{{"`Rust Formatting and Display Trait`"}} rust/lifetime_specifiers -.-> lab-99190{{"`Rust Formatting and Display Trait`"}} rust/method_syntax -.-> lab-99190{{"`Rust Formatting and Display Trait`"}} rust/traits -.-> lab-99190{{"`Rust Formatting and Display Trait`"}} end

Formatting

We've seen that formatting is specified via a format string:

  • format!("{}", foo) -> "3735928559"
  • format!("0x{:X}", foo) -> "0xDEADBEEF"
  • 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.

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 and their argument types in the std::fmt documentation.

Activity

Add an implementation of the fmt::Display trait for the Color struct above so that the output displays as:

RGB (128, 255, 90) 0x80FF5A
RGB (0, 3, 254) 0x0003FE
RGB (0, 0, 0) 0x000000

Three hints if you get stuck:

  • The formula for calculating a color in the RGB color space is: RGB = (R*65536)+(G*256)+B , (when R is RED, G is GREEN and B is BLUE). For more see RGB color format & calculation.
  • You may need to list each color more than once.
  • You can pad with zeros to a width of 2 with :0>2.

Summary

Congratulations! You have completed the Formatting lab. You can practice more labs in LabEx to improve your skills.

Other Rust Tutorials you may like