Step 4 of 6
Implementing Display
{} in println! and format! works with any type that implements the standard trait std::fmt::Display. Numbers and strings implement it; your types don't, until you write it. You met this briefly for error types; here's the full picture.
use std::fmt;
struct Money {
cents: i64,
}
impl fmt::Display for Money {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let sign = if self.cents < 0 { "-" } else { "" };
let abs = self.cents.abs();
write!(f, "{}${}.{:02}", sign, abs / 100, abs % 100)
}
}
fn main() {
let price = Money { cents: 1205 };
let refund = Money { cents: -350 };
println!("Price: {price}");
println!("Refund: {}", refund);
let text = price.to_string();
println!("{} characters", text.len());
}
Price: $12.05
Refund: -$3.50
6 characters
Piece by piece:
use std::fmt;lets you writefmt::Display,fmt::Formatterandfmt::Resultinstead of the full paths.- The one required method is
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result. Copy that signature; you'll write it many times. (Docs and editors often showFormatter<'_>; the'_is a lifetime, the topic of a later module, and you may leave it out.) fis where the text goes.write!(f, ...)works likeformat!but writes intof, and it returns thefmt::Resultyou need, so it's the last expression (no semicolon).- Implementing
Displayalso gives you.to_string()for free: the standard library implementsToStringfor every type that implementsDisplay.
Display or Debug?
Debug ({:?}) is for developers: derive it on almost every type and get a faithful dump of the fields. Display ({}) is for users: you choose the exact format, so it can't be derived. Should Money show $12.05, 12.05 USD or 12,05 €? Only you know.
Several lines? Use writeln!(f, ...)?; for each line except the last, then end with write!. The ? passes a formatting error on, like you did with Result in the errors module.
Common mistakes
- A semicolon after the last
write!. The method then returns()instead offmt::Result:
use std::fmt;
struct Tag(String);
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "#{}", self.0);
}
}
fn main() {
println!("{}", Tag(String::from("rust")));
}
error[E0308]: mismatched types
println!insidefmt. It prints straight to the screen instead of into the result, soformat!("{}", x)andx.to_string()get an empty string. Always write intof.write!(f, "{}", self)insideDisplayfor the same type. That callsfmtagain, forever, until the program crashes with a stack overflow. Format the fields, notself.- Using
{}on a type withoutDisplaygiveserror[E0277]: `Money` doesn't implement `std::fmt::Display`. The compiler suggests{:?}, which is fine for debugging but not for output users read.
Your task
Implement Display for Temperature so it shows the value with one decimal place followed by °C: 21.5°C, -3.0°C.
Challenge 2
Race results. Implement Display for RaceTime (a number of seconds) so it shows m:ss under an hour, like 1:05 or 0:09, and h:mm:ss from one hour on, like 1:01:05. Minutes and seconds after the first number always have two digits.
Challenge 3
Playing cards. Implement Display for both types:
Suitshows as♠,♥,♦or♣.Cardshows its rank then its suit: rank 1 isA, 11 isJ, 12 isQ, 13 isK, anything else is the number. SoA♠,10♥,Q♦.
Inside Card's fmt you can use {} on self.suit, because Suit implements Display too.
Previous: Traits as parameters: bounds and impl Trait Next: Multiple bounds, where clauses and returning impl Trait