In Rust, the Debug and Display traits control how types are formatted as strings. Debug is intended for programmer-facing output (useful for debugging and logging), while Display is for user-facing output. Understanding when and how to use each is essential for writing clear, professional Rust code.
The Debug trait can almost always be derived automatically using #[derive(Debug)], which generates a representation showing the type name and all field values. This is incredibly useful during development - you can print any Debug type using {:?} (compact) or {:#?} (pretty-printed with indentation).
The Display trait, however, must be implemented manually because there's no single "correct" way to display a type to users. You implement Display by writing a fmt method that uses format macros to build the output string. Once you implement Display, you automatically get a to_string() method for free.
use std::fmt;
// Debug - can be derived
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
// Display - must be implemented manually
impl fmt::Display for Point {
fn fmt(
&self,
f: &mut fmt::Formatter<'_>
) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
let p = Point { x: 3, y: 4 };
println!("{:?}", p); // Debug: Point { x: 3, y: 4 }
println!("{}", p); // Display: (3, 4){} - Uses Display trait{:?} - Uses Debug trait (compact){:#?} - Uses Debug trait (pretty-printed)Implement the following types demonstrating Debug and Display:
Create a Coordinate struct with x and y fields (both f64).
DebugDisplay to format as "(x, y)" (e.g., "(3.5, -2.0)")Create a Color enum with variants:
Red, Green, Blue (unit variants)
Rgb(u8, u8, u8) (RGB values)
Hex(String) (hex string like "#FF5733")
Derive Debug
Implement Display:
"Red", "Green", "Blue"Rgb displays as "rgb(r, g, b)"
(e.g., "rgb(255, 128, 0)")Hex displays as the hex string (e.g., "#FF5733")Create a Temperature struct that holds a value (f64) and a unit (TemperatureUnit enum).
The TemperatureUnit enum should have variants: Celsius, Fahrenheit, Kelvin.
Debug for bothDisplay for Temperature to format
as "value°C", "value°F", or "valueK"
(e.g., "25.5°C", "98.6°F", "300K")Create a LogLevel enum with variants: Error, Warning, Info, Debug.
DebugDisplay to show as uppercase strings:
"ERROR", "WARNING", "INFO", "DEBUG"Create a LogMessage struct with:
level: LogLevel
message: String
Derive Debug
Implement Display to format as
"[LEVEL] message" (e.g.,
"[ERROR] Connection failed")
Implement these functions to work with Debug and Display:
debug_string<T: std::fmt::Debug>( value: &T ) -> String
Debug typedisplay_string<T: std::fmt::Display>( value: &T ) -> String
Display typepretty_debug<T: std::fmt::Debug>( value: &T ) -> String
{:#?})// Coordinate
let coord = Coordinate { x: 3.5, y: -2.0 };
assert_eq!(format!("{}", coord), "(3.5, -2.0)");
assert_eq!(
format!("{:?}", coord),
"Coordinate { x: 3.5, y: -2.0 }"
);
// Color
let red = Color::Red;
let custom = Color::Rgb(255, 128, 0);
let hex = Color::Hex(String::from("#FF5733"));
assert_eq!(format!("{}", red), "Red");
assert_eq!(format!("{}", custom), "rgb(255, 128, 0)");
assert_eq!(format!("{}", hex), "#FF5733");
// Temperature
let temp = Temperature {
value: 25.5,
unit: TemperatureUnit::Celsius
};
assert_eq!(format!("{}", temp), "25.5°C");
// LogMessage
let log = LogMessage {
level: LogLevel::Error,
message: String::from("Connection failed"),
};
assert_eq!(
format!("{}", log),
"[ERROR] Connection failed"
);
// Utility functions
assert_eq!(
debug_string(&coord),
"Coordinate { x: 3.5, y: -2.0 }"
);
assert_eq!(display_string(&coord), "(3.5, -2.0)");#[derive(Debug)] on structs and enums for
automatic Debug implementationDisplay using
impl std::fmt::Display for TypeName { fn fmt(...) }write!(f, "format string", args) inside
the fmt methodmatch self { ... } to handle
each variant° is a valid UTF-8
character you can include directly in stringsformat!("{:?}", value) returns the Debug
stringformat!("{:#?}", value) returns the pretty-
printed Debug stringformat!("{}", value) returns the Display
stringvalue.to_string() for
Display types