Fixing the Rust Error: 'doesn't implement std::fmt::Display'

beginner🦀 Rust2026-06-08| Rust (any version), Cargo, Linux/macOS/Windows

Error Message

error[E0277]: `MyStruct` doesn't implement `std::fmt::Display`
#rust#compiler-error#debugging#traits#backend

TL;DR: The 10-Second Fix

If you just need to peek at your data for debugging, don't overthink it. Most developers solve this in two quick steps:

  • Add #[derive(Debug)] directly above your struct or enum.
  • Update your print statement to use the debug formatter: println!("{:?}", my_var);.

Need the output to look pretty for an end-user? You'll need to manually implement the Display trait instead.

The Problem: Why Rust is Complaining

Rust is obsessed with explicitness. Unlike languages like JavaScript or Python, Rust won't guess how you want to turn a complex data structure into a string. While common types like i32 or String have built-in formatting, your custom types are a blank slate.

You’ve probably hit this wall while trying to run code that looks like this:

struct User {
    id: u32,
    username: String,
}

fn main() {
    let user = User { id: 1, username: String::from("alice") };
    // This line triggers the E0277 error
    println!("{}", user);
}

When you use {}, you're asking Rust to use the Display trait. Since you haven't defined how User should look, the compiler stops you immediately with a message like this:

error[E0277]: `User` doesn't implement `std::fmt::Display`
  --> src/main.rs:10:20
   |
10 |     println!("{}", user);
   |                    ^^^^ `User` cannot be formatted with the default formatter

Solution 1: The Debug Trait (Best for Development)

The Debug trait is the standard way to inspect variables during development. It shows the struct name and all its internal fields without you having to write a single line of formatting logic.

How to apply it:

#[derive(Debug)] // 1. Just add this attribute
struct User {
    id: u32,
    username: String,
}

fn main() {
    let user = User { id: 1, username: String::from("alice") };
    // 2. Use {:?} for a single line, or {:#?} for a multi-line "pretty" view
    println!("{:?}", user);
}

The result: User { id: 1, username: "alice" }

For large structs with 20+ fields, always use {:#?}. It adds indentation and newlines, making the output much easier to scan in a crowded terminal.

Solution 2: Implementing Display (Best for Users)

If you're building a command-line tool, your users don't want to see User { id: 1, username: "alice" }. They just want the facts. Because every app has different needs, Rust requires you to write this logic manually.

How to implement it:

use std::fmt;

struct User {
    id: u32,
    username: String,
}

impl fmt::Display for User {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Format the output exactly how you want it
        write!(f, "User #{} ({})", self.id, self.username)
    }
}

fn main() {
    let user = User { id: 1, username: String::from("alice") };
    // Now the simple {} placeholder works perfectly
    println!("{}", user);
}

The result: User #1 (alice)

Why doesn't Rust do this automatically?

Safety and privacy are the core reasons. Imagine a struct containing a password_hash or a private_key. If Rust automatically implemented a string representation, you might accidentally leak sensitive data to your logs or console. By forcing you to choose between Debug and Display, Rust ensures you are intentional about what data leaves the program.

Checking Your Work

Follow these steps to ensure the error is gone for good:

  • Run cargo check. If the E0277 error is gone, your traits are satisfied.
  • Run cargo run to verify the visual output.
  • If you used Debug, verify that all fields are visible.
  • If you used Display, check that the formatting matches your write! macro logic.

Watch out for Nested Types

Rust traits are recursive. If your struct contains a custom sub-struct, that sub-struct must also implement the trait. If you forget, the compiler will complain about the parent struct even if it has the derive attribute.

#[derive(Debug)]
struct Metadata {
    created_at: u64,
}

#[derive(Debug)] // This works because Metadata also derives Debug
struct Task {
    name: String,
    meta: Metadata,
}

Helpful Resources

Related Error Notes