Skip to content

Rust Error E0404: Expected Trait, Found Struct or Derive Macro

E0404 means: Rust found the name you wrote, but it isn’t a trait, and only a trait can go in that spot. The spots are impl ___ for Type, a bound like T: ___, and dyn ___ or impl ___. The most common case surprises a lot of people: impl Debug for Point without an import. Debug is in scope, but as the #[derive(Debug)] macro, not as the trait. The other common case is a struct used as if it were a trait, which often happens when you’re used to classes and inheritance. Both are quick to fix. (This page is part of Rust compiler errors explained.)

error[E0404]: expected trait, found derive macro `Debug`
--> src/main.rs:6:6
|
6 | impl Debug for Point { // ❌ `Debug` here is the derive macro, not the trait
| ^^^^^ not a trait
|
help: consider importing this trait instead
|
1 + use std::fmt::Debug;
|

This is the program that produces it:

struct Point {
x: i32,
y: i32,
}
impl Debug for Point { // ❌ `Debug` here is the derive macro, not the trait
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Point at ({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 3, y: 4 };
println!("{:?}", p);
}

The ^^^^^ points at Debug on line 6, labelled “not a trait”. The headline says what Rust found instead: a derive macro called Debug.

That’s because there are two things named Debug. The macro Debug, which you use as #[derive(Debug)], is in the prelude, so every file can see it. The trait std::fmt::Debug, which you need for impl Debug for ..., is not in the prelude. So Rust finds the only Debug it can see, notices it’s a macro, and stops. The help line gives the import that brings in the trait. Hash works the same way: expected trait, found derive macro `Hash` , fixed with use std::hash::Hash;.

When the headline says found struct `Animal` (or enum, or type alias), the name is one of your own types, used where a trait belongs.

It’s like asking for “a licensed driver” and being handed a car. A car is a real thing, and closely related, but the job asked for someone who can drive: a set of abilities, which is what a trait is.

Rust separates data from behaviour. A struct or enum describes data: what a value is. A trait describes behaviour: what a type can do. impl Trait for Type connects the two: “this type can do these things”. A bound like T: Trait means “any type that can do these things”.

A struct can’t be used in those places, because it isn’t a list of abilities. And Rust has no inheritance, so impl Animal for Dog can’t mean “Dog is a kind of Animal” when Animal is a struct. If you want several types to share behaviour, the shared part is a trait. See Traits and Generics.

Rust also keeps macros and traits in separate namespaces, which is why Debug can be both a macro and a trait. The derive macro is in the prelude so #[derive(Debug)] works everywhere; the trait only needs to be named when you implement or bound it yourself.

Fix 1: Import the trait (use std::fmt::Debug;)

Section titled “Fix 1: Import the trait (use std::fmt::Debug;)”

Choose this when the message says found derive macro and you want your own format, or you need the trait in a bound like T: Debug. Add the line from the help:

use std::fmt::Debug;
struct Point {
x: i32,
y: i32,
}
impl Debug for Point {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Point at ({}, {})", self.x, self.y)
}
}
fn print_all<T: Debug>(items: &[T]) {
for item in items {
println!("{:?}", item);
}
}
fn main() {
let p = Point { x: 3, y: 4 };
println!("{:?}", p);
print_all(&[Point { x: 0, y: 0 }, Point { x: -1, y: 7 }]);
}

Output:

Point at (3, 4)
Point at (0, 0)
Point at (-1, 7)
▶ Try it Yourself

A common alternative is use std::fmt; and then impl fmt::Debug for Point with fmt::Formatter and fmt::Result. Both are fine; pick one style per file.

Fix 2: Use #[derive(Debug)] instead of writing the impl

Section titled “Fix 2: Use #[derive(Debug)] instead of writing the impl”

Choose this when you don’t need a custom format, which is most of the time for Debug. The derive macro writes the whole impl Debug for Point for you, and you don’t need any import:

#[derive(Debug)] // ✅ the derive macro writes `impl Debug for Point` for you
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 3, y: 4 };
println!("{:?}", p);
println!("{:#?}", p);
println!("distance from (0, 0) on a grid: {}", p.x.abs() + p.y.abs());
}

Output:

Point { x: 3, y: 4 }
Point {
x: 3,
y: 4,
}
distance from (0, 0) on a grid: 7
▶ Try it Yourself

Use one or the other, not both: #[derive(Debug)] plus your own impl Debug gives a “conflicting implementations” error (E0119). {:#?} is the pretty, multi-line form. More in Common Traits.

Fix 3: Define a trait for the shared behaviour

Section titled “Fix 3: Define a trait for the shared behaviour”

Choose this when the message says found struct in an impl X for Y line. You probably wanted Dog to be “a kind of Animal”, like a subclass:

error[E0404]: expected trait, found struct `Animal`
--> src/main.rs:7:6
|
7 | impl Animal for Dog { // ❌ `Animal` is a struct, not a trait
| ^^^^^^ not a trait

The program behind it:

struct Animal {
name: String,
}
struct Dog;
impl Animal for Dog { // ❌ `Animal` is a struct, not a trait
fn name(&self) -> String {
String::from("Rex")
}
}
fn main() {
println!("{}", Dog.name());
}

In Rust, the shared part is a trait: a list of methods each type must provide, plus optional default methods. The data stays in each struct:

trait Animal { // ✅ a trait: what every animal can do
fn name(&self) -> String;
fn greet(&self) -> String { // a default method
format!("Hi, I'm {}!", self.name())
}
}
struct Dog;
struct Cat {
name: String, // data lives in the struct
}
impl Animal for Dog {
fn name(&self) -> String {
String::from("Rex")
}
}
impl Animal for Cat {
fn name(&self) -> String {
self.name.clone()
}
}
fn main() {
println!("{}", Dog.greet());
let tom = Cat { name: String::from("Tom") };
println!("{}", tom.greet());
}

Output:

Hi, I'm Rex!
Hi, I'm Tom!
▶ Try it Yourself

If you only wanted to add methods to one struct, you don’t need a trait at all: write impl Dog { fn name(&self) -> String { ... } }, without for. See Defining a trait.

Fix 4: Take the struct directly, or bound on a trait

Section titled “Fix 4: Take the struct directly, or bound on a trait”

Choose this when the error points at a bound like T: Shape or impl Shape, and Shape is your struct:

error[E0404]: expected trait, found struct `Shape`
--> src/main.rs:5:16
|
5 | fn describe<T: Shape>(item: &T) { // ❌ `Shape` is a struct, not a trait
| ^^^^^ not a trait

The program behind it:

struct Shape {
sides: u32,
}
fn describe<T: Shape>(item: &T) { // ❌ `Shape` is a struct, not a trait
println!("a shape");
}
fn main() {
describe(&Shape { sides: 3 });
}

A bound says “any type that implements this trait”. If the function only ever gets a Shape, drop the generic and take &Shape. If it should work for many types, put the shared behaviour in a trait and bound on that:

struct Shape {
sides: u32,
}
fn describe(item: &Shape) { // ✅ take the struct itself, no generic needed
println!("a shape with {} sides", item.sides);
}
trait Named { // ✅ or a trait, for "any type that has a name"
fn name(&self) -> String;
}
impl Named for Shape {
fn name(&self) -> String {
match self.sides {
3 => String::from("triangle"),
4 => String::from("square"),
n => format!("{}-gon", n),
}
}
}
fn shout<T: Named>(item: &T) { // ✅ a trait bound needs a trait
println!("{}!", item.name().to_uppercase());
}
fn main() {
let t = Shape { sides: 3 };
describe(&t);
shout(&t);
shout(&Shape { sides: 6 });
}

Output:

a shape with 3 sides
TRIANGLE!
6-GON!
▶ Try it Yourself

Trait bounds are explained in Generics and Traits.

impl Vec<i32> as a parameter type. fn total(x: impl Vec<i32>) gives expected trait, found struct `Vec` . impl in that spot needs a trait. Fix: take &[i32] or Vec<i32>, or impl IntoIterator<Item = i32> if you want to accept any collection.

Box<dyn String>. dyn also needs a trait, so this gives expected trait, found struct `String` , and rustc suggests ToString. Fix: Box<dyn ToString> if you meant “anything that can become a string”, or just String. See Trait Objects.

A type alias for a trait. type Printable = dyn std::fmt::Display; followed by impl Printable for A gives expected trait, found type alias `Printable` with the label “type aliases cannot be used as traits”. Fix: write the trait’s real name in the impl.

Quick check

1. impl Debug for Point gives "expected trait, found derive macro Debug". What's the simplest fix if you want a custom format?

2. You have struct Animal and write impl Animal for Dog. What does Rust want instead?

3. Which of these can go after T: in fn show<T: ___>(x: T)?

Score: 0 / 3