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:

    // Import (via `use`) the `fmt` module to make it available.
    use core::fmt;

    // Define a structure for which `fmt::Display` will be implemented. This is
    // a tuple struct named `Structure` that contains an integer.
    #[derive(Drop)]
    struct Structure {
        value: u32,
    }

    // To use the `{}` marker, the trait `fmt::Display` must be implemented
    // manually for the type.
    impl StructureDisplay of fmt::Display<Structure> {
        // This trait requires `fmt` with this exact signature.
        fn fmt(self: @Structure, ref f: fmt::Formatter) -> Result<(), fmt::Error> {
            // 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.value)
        }
    }

fmt::Display may be cleaner than fmt::Debug but this presents a problem for the core library. How should ambiguous types be displayed? For example, if the core library implemented a single style for all Array<T>, what style should it be? Would it be either of these two?

  • Array<ContractAddress>: 0x123, 0x456, 0x789 (using hex representation)
  • Array<number>: 1,2,3 (using decimal representation)

No, because there is no ideal style for all types and the core library doesn't presume to dictate one. fmt::Display is not implemented for Array<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.

use core::fmt; // Import `fmt`

// A structure holding two numbers. `Debug` will be derived so the results can
// be contrasted with `Display`.
#[derive(Debug, Drop)]
struct MinMax {
    min: i64,
    max: i64,
}

// Implement `Display` for `MinMax`.
impl MinMaxDisplay of fmt::Display<MinMax> {
    fn fmt(self: @MinMax, ref f: fmt::Formatter) -> Result<(), fmt::Error> {
        // Use `self.number` to refer to each positional data point.
        write!(f, "({}, {})", *self.min, *self.max)
    }
}

// Define a structure where the fields are nameable for comparison.
#[derive(Debug, Drop)]
struct Point2D {
    x: i64,
    y: i64,
}

// Similarly, implement `Display` for `Point2D`.
impl Point2DDisplay of fmt::Display<Point2D> {
    fn fmt(self: @Point2D, ref f: fmt::Formatter) -> Result<(), fmt::Error> {
        // Customize so only `x` and `y` are denoted.
        write!(f, "x: {}, y: {}", *self.x, *self.y)
    }
}

fn main() {
    let minmax = MinMax { min: 0, max: 14 };

    println!("Compare structures:");
    println!("Display: {}", minmax);
    println!("Debug: {:?}", minmax);

    let big_range = MinMax { min: -300, max: 300 };
    let small_range = MinMax { min: -3, max: 3 };

    println!("The big range is {} and the small is {}", big_range, small_range);

    let point = Point2D { x: 3, y: 7 };

    println!("Compare points:");
    println!("Display: {}", point);
    // Error. Both `Debug` and `Display` were implemented, but `{:x}`
// requires `fmt::LowerHex` to be implemented. This will not work.
// println!("What does Point2D look like in binary: {:x}?", point);
}

So, fmt::Display has been implemented but fmt::LowerHex has not, and therefore cannot be used. core::fmt has many such traits and each requires its own implementation. This is detailed further in core::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:

Display: 3 + 7i
Debug: Complex { real: 3, imag: 7 }

See also:

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