Quick answer
You used a type somewhere that requires a specific trait (a capability, like being printable or comparable), and that type never implements it. Rust never infers a trait from a type's shape — it must be explicitly given via #[derive(...)] or a manual impl block. The error always names both the type and the exact missing trait; start there.
The exact error string
error[E0277]: `User` doesn't implement `Debug`
--> src/main.rs:8:22
|
8 | println!("{:?}", user);
| ^^^^ `User` cannot be formatted using `{:?}`
|
= help: the trait `Debug` is not implemented for `User`
= note: add `#[derive(Debug)]` to `User` or manually `impl Debug for User`
Every E0277 message follows this shape: it names the concrete type, the exact trait it's missing, and almost always a help:/note: pair suggesting the fix directly — frequently the exact #[derive(...)] line to add. Reading those two lines is usually enough to resolve the error without reasoning through the trait system from scratch.
Cause 1: a struct used with {:?} without deriving Debug
struct User { name: String, age: u32 }
let u = User { name: "Ada".into(), age: 30 };
println!("{:?}", u); // ❌ `User` doesn't implement `Debug`
// ✅ derive a default Debug implementation from the struct's fields:
#[derive(Debug)]
struct User { name: String, age: u32 }
This is by far the most common trigger for new Rust developers. The {:?} debug-format specifier in println!/format! requires the value's type to implement std::fmt::Debug, and a plain struct has no implementation of anything beyond what it explicitly derives or implements. #[derive(Debug)] is a compiler-generated implementation that prints a reasonable default representation (the struct name and each field), and it's idiomatic to add it to nearly every struct during development, purely so it can be printed for debugging.
Cause 2: a generic function's bound isn't satisfied by your type
fn summarize<T: std::fmt::Display>(item: T) {
println!("{item}");
}
struct Order { id: u32 }
summarize(Order { id: 1 }); // ❌ `Order` doesn't implement `Display`
// ✅ implement Display explicitly (Display has no derive — you control the format):
impl std::fmt::Display for Order {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Order #{}", self.id)
}
}
A generic type parameter like T: Display is a contract: only types that implement Display are allowed to fill that slot, and the compiler enforces it at every call site rather than at runtime. Built-in types (i32, String, f64) already implement most common traits in the standard library, which is why generic code "just works" with them and immediately breaks the moment you pass in your own type — your type genuinely hasn't opted into that capability yet.
Cause 3: comparing or sorting a type that doesn't implement PartialOrd/Ord
#[derive(Debug)]
struct Score(u32);
let mut scores = vec![Score(3), Score(1), Score(2)];
scores.sort(); // ❌ `Score` doesn't implement `Ord`
// ✅ derive the comparison traits too (requires PartialEq + Eq as prerequisites):
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Score(u32);
Sorting, min/max, and the comparison operators each depend on a specific trait (Ord for a total ordering, PartialOrd for a partial one), and none of them are implied by simply defining a struct — Rust has no notion of "comparable by default" the way some languages compare objects by reference identity as a fallback. Deriving multiple related traits together (PartialEq, Eq, PartialOrd, Ord) is the normal pattern, since Ord itself requires the others as prerequisites; the compiler's error will name exactly which one is still missing if you derive only some of them.
Cause 4: an operator (+, ==, etc.) used on a type that hasn't opted in
struct Point { x: i32, y: i32 }
let a = Point { x: 1, y: 2 };
let b = Point { x: 3, y: 4 };
let c = a + b; // ❌ `Point` doesn't implement `Add`
// ✅ implement the operator trait explicitly — you decide what "+" means for it:
use std::ops::Add;
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point {
Point { x: self.x + other.x, y: self.y + other.y }
}
}
Every operator in Rust (+, -, ==, indexing with [], and more) is really just syntax sugar for a trait method call (Add::add, PartialEq::eq, Index::index), and none of these are defined for a custom type unless you implement them. This is the same "explicit opt-in, no structural inference" principle running through every cause on this page: the compiler will never guess that two Point values should be added field-by-field just because that seems like the obvious behavior — you have to say so.
Common causes at a glance
| Missing trait | Triggered by | Fix |
|---|---|---|
Debug | println!("{:?}", x) | #[derive(Debug)] |
Display | println!("{x}"), a generic bound | manual impl Display (no derive exists) |
PartialEq / Eq | ==, assert_eq! | #[derive(PartialEq, Eq)] |
PartialOrd / Ord | .sort(), </> | #[derive(PartialOrd, Ord)] (plus PartialEq/Eq) |
Clone / Copy | .clone(), implicit copy on use | #[derive(Clone)] (Copy only for simple stack-only data) |
Add/Sub/etc. | +, -, other operators | manual impl Add for T (define the behavior yourself) |
Why this happens: Rust traits are nominal, not structural
The unifying reason every cause above requires an explicit derive or impl is Rust's choice of nominal typing for traits, as opposed to a structural system that would grant a capability automatically to any type whose shape happens to match. A structural approach (as in some languages' interfaces) would let two unrelated types silently become interchangeable the moment their fields happened to line up — convenient sometimes, but a source of subtle bugs when the resemblance is coincidental rather than intentional. By requiring every trait implementation to be a deliberate, visible statement in the code (either handwritten or requested via #[derive(...)]), Rust guarantees that any capability a type has was actually decided by whoever wrote that type, and that a reader can find the exact logic (or auto-generated default) backing any operation performed on a value. E0277 is simply the compiler enforcing that guarantee at every use site.
Debugging checklist
- ✓ Read the exact type and exact trait named in the error — both are always stated
- ✓ Check the
help:/note:lines — they often name the exact#[derive(...)]to add - ✓ If it's
Debug,Clone,PartialEq/Eq, orPartialOrd/Ord, try deriving it first - ✓ If it's
Displayor an operator trait, write the manualimplyourself — these have no derive - ✓ In a generic function, confirm the bound (
T: SomeTrait) actually matches what the function body calls onT
Frequently Asked Questions
What does "the trait bound X: Y is not satisfied" mean?
Some code — a function call, a generic constraint, an operator, a formatting macro — requires that type X implements trait Y (a defined set of capabilities, like being printable, comparable, or cloneable), and the compiler has determined X does not implement Y. Traits in Rust are never inferred structurally; a type only has a capability if something explicitly implements that trait for it.
Why doesn't Rust just check if my struct has the right fields, the way some languages check structurally?
Rust deliberately uses nominal typing for traits: a type must explicitly declare (via impl or #[derive]) that it provides a trait's behavior, rather than the compiler inferring it from shape alone. This prevents two unrelated types that merely look similar from being silently treated as interchangeable, and lets a trait's implementation encode real, type-specific logic (like how to actually compare two instances for equality) rather than a shallow field-by-field default that might be wrong for that type.
Why does println!("{:?}", my_struct) fail with this error?
The {:?} debug-format specifier requires the value's type to implement the Debug trait, and a plain struct doesn't get that for free. Adding #[derive(Debug)] directly above the struct definition asks the compiler to auto-generate a reasonable Debug implementation from the struct's fields, which resolves this specific and extremely common case in one line.
Why does a generic function suddenly fail to compile when I call it with my own type?
A generic function's type parameter only has the capabilities explicitly listed in its trait bounds (fn process<T: Display>(x: T) can only call Display methods on x) — it works with built-in types like i32 or String because the standard library already implements common traits for them, but your own type needs the same traits implemented (or derived) before it can be used the same way.
Can I satisfy a trait bound by borrowing instead of implementing the trait?
Sometimes — many traits are automatically implemented for a reference to a type that implements them (a blanket impl), so passing &value instead of value can satisfy a bound whose real dependency is a shared reference, not ownership. This doesn't help if the bound genuinely requires functionality your type has never implemented at all, only if the issue is really about ownership versus borrowing.
Does the compiler tell me which trait is missing?
Yes, always — the error explicitly names both the type and the exact trait it fails to implement, and very often adds a "help: the trait X is not implemented for Y" note plus a suggestion (like adding a specific #[derive(...)] attribute) directly in the compiler output, making this one of the more self-diagnosing errors in the language once you know to read past the first line.
More Go, Rust & Java errors
Browse the full reference for Go, Rust, Java, and other backend language errors — exact message, cause, and fix.