Question
How can you print custom structs and arrays in Rust? For example, how should the following struct and fixed-size array be displayed with println!?
struct MyStruct {
a: i32,
b: i32,
}
let arr: [i32; 10] = [1; 10];
Short Answer
Rust does not automatically choose a text representation for every custom type. You will learn to print arrays with the Debug formatter, make a struct printable with #[derive(Debug)], and choose between the Debug ({:?}) and Display ({}) formatting traits.
Concept
Rust formatting is trait-based. A formatting placeholder in println! tells Rust which formatting trait the value must implement.
{:?}uses theDebugtrait. It is intended for developers: logging, inspecting values, and troubleshooting.{}uses theDisplaytrait. It is intended for user-facing, readable output.
Arrays whose element types implement Debug, including [i32; 10], can be printed with {:?}. A custom struct does not implement Debug automatically because Rust requires you to explicitly opt in. For ordinary structs, #[derive(Debug)] asks the compiler to generate that implementation.
This explicit approach matters because a type's output may reveal sensitive fields, be expensive to build, or need a carefully designed user-facing format.
Mental Model
Think of a value as an object that can be described in different languages.
Debugis a technician's report: it shows the structure and field values so a developer can inspect what happened.Displayis a polished label for an end user: it shows only the information and wording you choose.
println! needs to know which description to request. {:?} requests the technician's report, while {} requests the polished label.
Syntax and Examples
Use {:?} for a type that implements Debug.
#[derive(Debug)]
struct MyStruct {
a: i32,
b: i32,
}
fn main() {
let item = MyStruct { a: 10, b: 20 };
let arr: [i32; 10] = [1; 10];
println!("Struct: {:?}", item);
println!("Array: {:?}", arr);
}
Possible output:
Struct: MyStruct { a: 10, b: 20 }
Array: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
For multi-line debug output, use {:#?}:
println!("{:#?}", item);
This produces a more indented, readable representation for nested values.
Step by Step Execution
Consider this program:
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 3, y: 7 };
let values = [4, 8, 15];
println!("point = {:?}", point);
println!("values = {:?}", values);
}
#[derive(Debug)]generates an implementation ofDebugforPoint.pointis created withxequal to3andyequal to7.valuesis an array of threei32values.i32implementsDebug, so the array can also implementDebug.- The first
println!sees{:?}and callsDebugformatting forpoint. - The second
println!callsDebugformatting for the array.
Output:
point = Point { x: 3, y: 7 }
values = [4, 8, 15]
Real World Use Cases
- Debugging API data: Print a deserialized response struct while developing an HTTP client.
- Inspecting configuration: Log an array of enabled ports or feature flags.
- Testing: Include
{:?}in assertion messages so failed tests show the actual value. - Command-line tools: Use
Displayfor normal output andDebugwhen a--verboseoption is enabled. - Data processing: Inspect batches of parsed values before writing them to a database or file.
Real Codebase Usage
In production Rust code, developers usually derive Debug broadly on application data types because it helps with diagnostics and test failures.
#[derive(Debug, Clone, PartialEq)]
struct User {
id: u64,
name: String,
}
Common patterns include:
-
Logging values during failures:
eprintln!("Could not process request: {:?}", request); -
Using
Debugin assertions:assert_eq!(actual, expected, "actual value: {:?}", actual); -
Implementing
Displayfor user-facing errors:use std::fmt; struct InvalidAge; impl fmt::Display for InvalidAge { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "age must be at least 18") } }
Be deliberate with sensitive data. A derived Debug output can include passwords, tokens, and personal data if those fields are present. Avoid logging such values or implement a custom Debug formatter that redacts them.
Common Mistakes
Using {} when only Debug is available
This does not compile because deriving Debug does not implement Display:
#[derive(Debug)]
struct MyStruct {
a: i32,
b: i32,
}
let value = MyStruct { a: 1, b: 2 };
println!("{}", value); // Error: Display is not implemented
Use {:?} for debug output, or implement Display when you need {}.
Forgetting #[derive(Debug)]
struct MyStruct {
a: i32,
b: i32,
}
let value = MyStruct { a: 1, b: 2 };
println!("{:?}", value); // Error: Debug is not implemented
Add #[derive(Debug)] above the struct definition.
Printing an array with a non-debug element type
An array can be debug-printed only when its elements can be debug-printed. If a custom element type lacks Debug, deriving Debug for that element type is usually the fix.
Treating Debug as a stable file or API format
Debug output is for developers. Do not parse it or rely on its exact text for saved data, network protocols, or tests that require a stable format.
Logging secrets accidentally
#[derive(Debug)] prints all printable fields by default. Do not debug-print authentication tokens, passwords, or private keys in logs.
Comparisons
| Option | Placeholder | Purpose | Who implements it? |
|---|---|---|---|
Debug | {:?} | Developer inspection and diagnostics | Often generated with #[derive(Debug)] |
Pretty Debug | {:#?} | Multi-line developer inspection | Same Debug implementation |
Display | {} | User-facing text | Usually implemented manually |
Debug is usually the best first choice for printing structs during development. Choose Display when the wording, layout, or included fields must be intentional.
use std::fmt;
struct Temperature(f64);
impl fmt::Display for Temperature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:.1} °C", self.0)
}
}
fn main() {
let temp = Temperature(21.75);
println!("{}", temp);
}
This prints 21.8 °C, rather than a developer-oriented struct representation.
Cheat Sheet
// Print an array with Debug
let arr = [1, 2, 3];
println!("{:?}", arr);
// Allow a custom struct to use Debug
#[derive(Debug)]
struct User {
id: u32,
}
let user = User { id: 42 };
println!("{:?}", user);
// Pretty, multi-line Debug output
println!("{:#?}", user);
- Use
{:?}when the type implementsstd::fmt::Debug. - Add
#[derive(Debug)]to most simple structs and enums. - Use
{}only when the type implementsstd::fmt::Display. Debugis for developers; do not use it as a stable serialization format.- Arrays can be formatted when their element type supports the requested formatting trait.
FAQ
Why can I print an array with {:?} in Rust?
Rust arrays implement Debug when their element type implements Debug. Since i32 implements Debug, [i32; 10] does too.
Why does println!("{}", my_struct) fail?
{} requires the Display trait. Custom structs do not get Display automatically, and #[derive(Debug)] only provides Debug for {:?}.
How do I make a Rust struct printable?
For developer output, add #[derive(Debug)] and print it with {:?}. For user-facing output, implement std::fmt::Display.
Can I use both Debug and Display on one struct?
Yes. Derive or implement Debug, then implement Display separately when you need custom user-facing text.
What is the difference between {:?} and {:#?}?
Both use Debug. {:#?} requests alternate, usually multi-line and indented, debug output.
Should I derive Debug for every struct?
It is common and useful for many application types. Review types containing secrets or sensitive data before deriving it, because debug output can expose fields in logs.
Can I print only selected struct fields?
Yes. Print fields directly:
println!("a = {}, b = {}", value.a, value.b);
Or implement Display for a reusable custom format.
Mini Project
Description
Build a small inventory report for a command-line program. The program stores products in structs, keeps several products in an array, and prints both a developer-oriented report and a concise user-facing message.
Goal
Print an array of product structs with Debug and print each product with a custom Display format.
Requirements
Create a Product struct with a name, quantity, and price.
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.