Question

How to Print a Vec in Rust: Display vs Debug

rustprintln

Question

I tried to print a Rust vector using println!("{}", v2), but the compiler reports that Vec<T> does not implement std::fmt::Display.

fn main() {
    let v2 = vec![1; 10];
    println!("{}", v2);
}

The compiler error says:

error[E0277]: `std::vec::Vec<{integer}>` doesn't implement `std::fmt::Display`
 --> src/main.rs:3:20
  |
3 |     println!("{}", v2);
  |                    ^^ `std::vec::Vec<{integer}>` cannot be formatted with the default formatter
  |
  = help: the trait `std::fmt::Display` is not implemented for `std::vec::Vec<{integer}>`
  = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
  = note: required by `std::fmt::Display::fmt`

Why does this happen, and is Display implemented for Vec<T>? If not, how should a vector be printed in Rust?

Short Answer

By the end of this page, you will understand why Vec<T> cannot be printed with {} in Rust, when to use {:?} instead, and how Display differs from Debug. You will also learn practical ways to print vectors, including pretty-printing and creating custom output when you want a human-friendly format.

Concept

Rust has two common formatting traits for printing values:

  • Display → for user-facing, clean output with {}
  • Debug → for developer-facing output with {:?}

A Vec<T> implements Debug, but not Display.

That means this works:

let v = vec![1, 2, 3];
println!("{:?}", v);

But this does not:

let v = vec![1, 2, 3];
println!("{}", v); // error

Why Vec<T> does not implement Display

Display is meant to produce a single, clear, human-readable representation. For a vector, there is no one obvious display format:

  • Should it print as [1, 2, 3]?
  • Or 1, 2, 3?
  • Or one item per line?
  • Should strings be quoted?
  • Should nested vectors be indented?

Because different programs want different output styles, Rust does not choose one default Display format for Vec<T>.

Why Debug is implemented

Debug exists specifically to help developers inspect values while coding, testing, or debugging. A vector has a natural debug representation, so Rust provides one.

Example:

let v = vec![1, 2, 3];
println!("{:?}", v);   // [1, 2, 3]
println!("{:#?}", v);  // pretty printed

Can you implement Display for Vec<T> yourself?

Not directly.

Because both the trait (Display) and the type (Vec<T>) are defined outside your crate, Rust's orphan rules prevent this implementation:

// Not allowed
impl<T> std::fmt::Display for Vec<T> {
    // ...
}

If you want custom display formatting, the usual solution is to create your own wrapper type and implement Display for that wrapper.

Mental Model

Think of Display and Debug as two different labels on a printer:

  • Display = show this nicely to a user
  • Debug = show this clearly to a programmer

A Vec<T> is like a box full of items. If someone says, "print the box nicely," Rust asks: what does nicely mean?

  • As a comma-separated list?
  • As JSON-like text?
  • As a vertical list?

There is no single best answer, so Rust refuses to guess for Display.

But for debugging, a standard inspected form is fine, so Debug prints the contents in a consistent developer-friendly way.

Take Quiz

Syntax and Examples

The most important syntax is:

println!("{:?}", value);   // Debug
println!("{:#?}", value);  // Pretty Debug
println!("{}", value);     // Display

Printing a vector with Debug

fn main() {
    let v = vec![1, 2, 3];
    println!("{:?}", v);
}

Output:

[1, 2, 3]

Pretty-printing a vector

fn main() {
    let v = vec![1, 2, 3];
    println!("{:#?}", v);
}

Output:

[
    1,
    2,
    3,
]

Creating custom display output

If you want output like 1, 2, 3, you can format the elements yourself:

fn main() {
    let v = vec![1, 2, 3];
    let text = v.iter()
        .map(|n| n.to_string())
        .collect::<Vec<_>>()
        .join(", ");

    println!("{}", text);
}

Output:

1, 2, 3

Wrapper type for reusable Display

use std::fmt;

struct DisplayVec<T>(Vec<T>);

impl<T: fmt::Display> fmt::Display for DisplayVec<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[")?;
        for (i, item) in self.0.iter().enumerate() {
            if i > 0 {
                write!(f, ", ")?;
            }
            write!(f, "{}", item)?;
        }
        write!(f, "]")
    }
}

fn main() {
    let v = DisplayVec(vec![1, 2, 3]);
    println!("{}", v);
}

This works because DisplayVec is your own type, so Rust allows you to implement Display for it.

Step by Step Execution

Consider this example:

fn main() {
    let v = vec![1, 2, 3];
    println!("{:?}", v);
}

Step by step

  1. vec![1, 2, 3] creates a Vec<i32>.
  2. println! sees the format string "{:?}".
  3. {:?} tells Rust: use the Debug trait.
  4. Vec<i32> implements Debug, so formatting succeeds.
  5. Rust prints the vector in debug form: [1, 2, 3].

Now compare it with this failing version:

fn main() {
    let v = vec![1, 2, 3];
    println!("{}", v);
}

What changes?

  1. println! sees "{}".
  2. {} tells Rust: use the Display trait.
  3. Vec<i32> does not implement Display.
  4. Compilation stops with error E0277.

So the issue is not with vectors themselves. The issue is that the chosen formatting placeholder does not match the trait the type supports.

Real World Use Cases

Printing vectors appears in many practical Rust tasks:

Logging intermediate data

When debugging APIs, parsers, or algorithms, developers often inspect vectors:

println!("Parsed tokens: {:?}", tokens);

Inspecting query or processing results

A function may return a list of records, IDs, or errors:

println!("Matching IDs: {:?}", ids);

Pretty-printing nested data during development

When vectors contain structs or nested collections, {:#?} is easier to read:

println!("{:#?}", results);

Building user-facing output

If you are printing data for users, you usually do not rely on Debug. Instead, you format the output explicitly:

let names = vec!["Alice", "Bob", "Cara"];
println!("Users: {}", names.join(", "));

In real applications, Debug is mostly for developers, while custom formatting is used for reports, CLI output, and UI text.

Take Quiz

Real Codebase Usage

In real Rust codebases, developers usually follow these patterns:

Use Debug for quick inspection

During development, Vec<T> is often printed with {:?} in:

  • temporary debugging statements
  • tests
  • logs
  • prototypes
dbg!(&items);
println!("Loaded items: {:?}", items);

Format user output explicitly

For command-line tools or messages shown to users, teams usually choose a deliberate format:

let output = values
    .iter()
    .map(|v| v.to_string())
    .collect::<Vec<_>>()
    .join(", ");

println!("Values: {}", output);

Use wrapper types for reusable formatting

If the same vector formatting is needed in many places, a wrapper type is common:

struct CsvVec<T>(Vec<T>);

Then Display can be implemented once and reused.

Use Debug bounds in generic code

In generic helper functions, developers often require T: Debug when they only need inspectable output:

use std::fmt::Debug;

fn log_items<T: Debug>(items: &[T]) {
    println!("{:?}", items);
}

Prefer slices in APIs when ownership is unnecessary

A lot of printing helpers take &[T] instead of Vec<T> because they only need to read elements:

fn print_numbers(nums: &[i32]) {
    println!("{:?}", nums);
}

This makes the code more flexible and works with vectors, arrays, and slices.

Common Mistakes

Mistake 1: Using {} for a vector

Broken code:

fn main() {
    let v = vec![1, 2, 3];
    println!("{}", v);
}

Why it fails:

  • {} requires Display
  • Vec<T> does not implement Display

Fix:

println!("{:?}", v);

Mistake 2: Assuming Debug is good for user-facing output

This works:

println!("{:?}", vec!["Alice", "Bob"]);

But the output may not be the style you want for end users.

Better:

let names = vec!["Alice", "Bob"];
println!("{}", names.join(", "));

Mistake 3: Trying to implement Display directly for Vec<T>

Broken idea:

use std::fmt;

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

Why it fails:

  • Display is from the standard library
  • Vec<T> is also from the standard library
  • Rust does not let you implement a foreign trait for a foreign type

Use a wrapper type instead.

Mistake 4: Forgetting element formatting requirements

If you build custom output, each element must itself be printable.

Example:

let text = values
    .iter()
    .map(|v| v.to_string())
    .collect::<Vec<_>>()
    .join(", ");

This requires the element type to support Display.

Mistake 5: Confusing Debug and Display

A type can implement:

  • both Debug and Display
  • only Debug
  • only Display (less common)

Always match the placeholder to the trait:

  • {} → Display
  • {:?} → Debug

Comparisons

FeatureDisplayDebug
Placeholder{}{:?}
PurposeUser-facing outputDeveloper-facing output
Intended styleClean, readableInspectable, diagnostic
Implemented for Vec<T>NoYes
Pretty-print optionNo built-in equivalent{:#?}
Good for logs/testsSometimesYes
Good for final CLI/UI textYesUsually not

{:?} vs {:#?}

FormatterUse caseExample output
{:?}Compact debug printing[1, 2, 3]
{:#?}Multi-line readable debug output`[
1,
2,
3,

]` |

Custom formatting vs Debug

ApproachBest whenTrade-off
{:?}You want quick inspectionOutput style is fixed
join(", ") or manual formattingYou want user-friendly textMore code
Wrapper + DisplayYou need reusable formattingRequires a custom type
Take Quiz

Cheat Sheet

  • println!("{}", value) uses Display
  • println!("{:?}", value) uses Debug
  • println!("{:#?}", value) uses pretty Debug
  • Vec<T> implements Debug
  • Vec<T> does not implement Display
  • You cannot implement Display for Vec<T> directly because of Rust's orphan rules
  • For custom vector output:
    • format elements manually
    • or create a wrapper type and implement Display

Common patterns

let v = vec![1, 2, 3];
println!("{:?}", v);
println!("{:#?}", v);
let text = v.iter()
    .map(|x| x.to_string())
    .collect::<Vec<_>>()
    .join(", ");
println!("{}", text);

Rule of thumb

  • Use {:?} for debugging
  • Use custom formatting for user-facing output
  • Use a wrapper type if you need reusable Display behavior

FAQ

Why can't I print a Vec with {} in Rust?

Because {} requires the Display trait, and Vec<T> does not implement Display.

How do I print a vector in Rust?

Use {:?} for debug output:

println!("{:?}", my_vec);

What is the difference between Display and Debug in Rust?

Display is for clean user-facing output. Debug is for programmer-facing inspection.

Can I implement Display for Vec<T> myself?

No, not directly. Rust's orphan rules prevent implementing a foreign trait for a foreign type. Use a wrapper type instead.

What does {:#?} do in Rust?

It pretty-prints Debug output across multiple lines, which is useful for complex or nested data.

Should I use Debug output in production apps?

It is fine for logs and diagnostics, but for user-visible output you should usually create a custom format.

Does every type implement Debug in Rust?

No. Many standard types do, but not all custom types automatically do unless you derive or implement Debug.

Is a slice printable the same way as a vector?

Usually yes for debug formatting. Both slices and vectors can be printed with {:?} when their element type supports Debug.

Take Quiz

Mini Project

Description

Build a small Rust program that prints a list of tasks in two ways: a developer-friendly debug view and a clean user-facing view. This demonstrates when to use Debug and when to build custom formatting instead of relying on Display for Vec<T>.

Goal

Create a program that stores tasks in a vector, prints the raw vector with Debug, and then prints a custom comma-separated string for users.

Requirements

  • Create a vector of at least three task names.
  • Print the vector once using {:?}.
  • Print the tasks again in a user-friendly comma-separated format.
  • Include a wrapper type or helper function for reusable formatting.
Take Quiz

Keep learning

Related questions

View more