Skip to content

← Generics and Traits step 6 of 24

Easy Primitives

Format money with Display

Implement std::fmt::Display for the supplied Money type. Replace the starter's direct ToString implementation, and keep render calling .to_string() on each Money value.

pub struct Money { pub cents: i64 }
pub fn render(cents: Vec<i64>) -> Vec<String>

Required format

Each input is an integer number of cents, anywhere in the i64 range. Return one string per input, preserving order and duplicates. Empty input produces an empty vector.

Put $ before the whole-dollar amount, commas between groups of three whole-dollar digits, and exactly two digits after the decimal point. Negative amounts have a minus sign before $. Do not add a plus sign, spaces or a newline. Use these fixed symbols regardless of locale.

Cents Output
123456 $1,234.56
0 $0.00
5 $0.05
-123456 -$1,234.56
100000000 $1,000,000.00
i64::MIN -$92,233,720,368,547,758.08

This exercise requires plain {} formatting and .to_string(). Support for extra width, alignment, precision or other formatting flags is outside the task. No floating-point conversion or rounding is needed.

Display also supplies ToString

Rust's formatting traits include Display for {} and Debug for {:?}, as well as traits for formats such as binary and hexadecimal. Debug is commonly derived for diagnostics. Display describes a chosen text format; there is no built-in Display derive, although external macros can generate implementations.

The standard library implements ToString for types that implement Display, including unsized types. This is a blanket implementation: it applies to every type meeting its bound. It provides the method automatically, not an allocation-free conversion. See the ToString documentation.

Here is a complete example with a simpler format:

use std::fmt;

struct Count(u32);

impl fmt::Display for Count {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} items", self.0)
    }
}

fn main() {
    let count = Count(3);
    assert_eq!(count.to_string(), "3 items");
    assert_eq!(format!("Count: {count}"), "Count: 3 items");
}

A direct ToString implementation is possible for a type without Display, but Clippy recommends implementing Display instead. Keeping both would conflict with the standard library's blanket implementation. An inherent to_string method is also discouraged and can shadow the trait method. Remove the starter's direct implementation rather than adding Display beside it. Its todo!() also panics when a nonempty input reaches it.

Handle the smallest negative value

i64::MIN has no positive counterpart in i64. Negating it or taking its signed absolute value overflows. With overflow checks enabled this panics; with checks disabled it wraps. Optimization alone does not guarantee which behavior a build uses, since overflow checks can be configured explicitly.

Use cents.unsigned_abs() to obtain the magnitude as u64. Divide by 100 for whole dollars and take the remainder modulo 100 for cents. Group the whole-dollar decimal digits from the right. Format the remainder with :02 to retain a leading zero when needed.

Write fields and propagate errors

Within Money's Display implementation, write!(f, "{}", self) would call that same implementation again. Format the magnitude and sign instead. Clippy's recursive_format_impl detects this direct recursion.

Each write returns fmt::Result. Propagate intermediate errors with ? and return the final write's result. A failing destination is an error; a valid negative amount is not. Do not add a newline to this exercise's output. See Display's error contract.

Passing requires correct output, compilation and Clippy with warnings treated as errors.