Rust-Fehler E0404: expected trait, found struct or derive macro (kein Trait)
E0404 bedeutet: Rust hat den Namen gefunden, den du geschrieben hast, aber er ist kein Trait, und an dieser Stelle darf nur ein Trait stehen. Solche Stellen sind impl ___ for Type, ein Bound wie T: ___ sowie dyn ___ oder impl ___. Der häufigste Fall überrascht viele: impl Debug for Point ohne Import. Debug ist im Gültigkeitsbereich, aber als Makro #[derive(Debug)], nicht als Trait. Der andere häufige Fall ist ein Struct, das wie ein Trait benutzt wird. Das passiert oft, wenn du an Klassen und Vererbung gewöhnt bist. Beides ist schnell behoben. (Diese Seite gehört zur Reihe Rust-Compilerfehler einfach erklärt.)
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“error[E0404]: expected trait, found derive macro `Debug` --> src/main.rs:6:6 |6 | impl Debug for Point { // ❌ `Debug` ist hier das Derive-Makro, nicht der Trait | ^^^^^ not a trait |help: consider importing this trait instead |1 + use std::fmt::Debug; |Auf Deutsch: „Trait erwartet, Derive-Makro Debug gefunden.“ Das Label sagt „kein Trait“, und die Hilfe: „Importiere stattdessen am besten diesen Trait.“
Dieses Programm erzeugt den Fehler:
struct Point { x: i32, y: i32,}
impl Debug for Point { // ❌ `Debug` ist hier das Derive-Makro, nicht der 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);}Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“Die ^^^^^ zeigen in Zeile 6 auf Debug, mit dem Label „not a trait“. Die Überschrift sagt, was Rust stattdessen gefunden hat: ein Derive-Makro namens Debug.
Das liegt daran, dass es zwei Dinge namens Debug gibt. Das Makro Debug, das du als #[derive(Debug)] benutzt, steht im Prelude, also sieht es jede Datei. Der Trait std::fmt::Debug, den du für impl Debug for ... brauchst, steht nicht im Prelude. Rust findet also das einzige Debug, das es sehen kann, merkt, dass es ein Makro ist, und hört auf. Die Hilfe-Zeile liefert den Import, der den Trait hereinholt. Bei Hash ist es genauso: expected trait, found derive macro `Hash` , behoben mit use std::hash::Hash;.
Sagt die Überschrift found struct `Animal` (oder enum, oder type alias), dann ist der Name einer deiner eigenen Typen, und er steht dort, wo ein Trait hingehört.
Das ist, als suchst du „jemanden mit Führerschein“ und bekommst ein Auto. Ein Auto ist etwas Echtes und hat auch viel damit zu tun, aber gesucht war jemand, der fahren kann: eine Reihe von Fähigkeiten. Genau das ist ein Trait.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“Rust trennt Daten von Verhalten. Ein Struct oder Enum beschreibt Daten: was ein Wert ist. Ein Trait beschreibt Verhalten: was ein Typ kann. impl Trait for Type verbindet beides: „Dieser Typ kann diese Dinge.“ Ein Bound wie T: Trait heißt „jeder Typ, der diese Dinge kann“.
Ein Struct kann an diesen Stellen nicht stehen, weil es keine Liste von Fähigkeiten ist. Und Rust hat keine Vererbung, also kann impl Animal for Dog nicht „Dog ist eine Art Animal“ bedeuten, wenn Animal ein Struct ist. Sollen mehrere Typen Verhalten teilen, dann ist der gemeinsame Teil ein Trait. Siehe Traits und Generics.
Rust hält Makros und Traits außerdem in getrennten Namensräumen. Deshalb kann Debug gleichzeitig ein Makro und ein Trait sein. Das Derive-Makro steht im Prelude, damit #[derive(Debug)] überall funktioniert. Den Trait musst du nur nennen, wenn du ihn selbst implementierst oder als Bound benutzt.
So behebst du den Fehler
Abschnitt betitelt „So behebst du den Fehler“Lösung 1: Den Trait importieren (use std::fmt::Debug;)
Abschnitt betitelt „Lösung 1: Den Trait importieren (use std::fmt::Debug;)“Nimm diese Lösung, wenn die Meldung found derive macro sagt und du ein eigenes Format willst, oder wenn du den Trait in einem Bound wie T: Debug brauchst. Ergänze die Zeile aus der Hilfe:
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, "Punkt bei ({}, {})", 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 }]);}Ausgabe:
Punkt bei (3, 4)Punkt bei (0, 0)Punkt bei (-1, 7)Eine verbreitete Alternative ist use std::fmt; und dann impl fmt::Debug for Point mit fmt::Formatter und fmt::Result. Beides ist in Ordnung; nimm einen Stil pro Datei.
Lösung 2: #[derive(Debug)] benutzen, statt das impl zu schreiben
Abschnitt betitelt „Lösung 2: #[derive(Debug)] benutzen, statt das impl zu schreiben“Nimm diese Lösung, wenn du kein eigenes Format brauchst. Bei Debug ist das meistens so. Das Derive-Makro schreibt das ganze impl Debug for Point für dich, und du brauchst keinen Import:
#[derive(Debug)] // ✅ das Derive-Makro schreibt `impl Debug for Point` für dichstruct Point { x: i32, y: i32,}
fn main() { let p = Point { x: 3, y: 4 }; println!("{:?}", p); println!("{:#?}", p); println!("Abstand von (0, 0) im Raster: {}", p.x.abs() + p.y.abs());}Ausgabe:
Point { x: 3, y: 4 }Point { x: 3, y: 4,}Abstand von (0, 0) im Raster: 7Nimm das eine oder das andere, nicht beides: #[derive(Debug)] plus dein eigenes impl Debug ergibt einen Fehler „conflicting implementations“ (widersprüchliche Implementierungen, E0119, auf Englisch). {:#?} ist die hübsche, mehrzeilige Form. Mehr dazu unter Wichtige Traits.
Lösung 3: Einen Trait für das gemeinsame Verhalten definieren
Abschnitt betitelt „Lösung 3: Einen Trait für das gemeinsame Verhalten definieren“Nimm diese Lösung, wenn die Meldung bei einer Zeile impl X for Y von found struct spricht. Wahrscheinlich wolltest du, dass Dog „eine Art Animal“ ist, wie eine Unterklasse:
error[E0404]: expected trait, found struct `Animal` --> src/main.rs:7:6 |7 | impl Animal for Dog { // ❌ `Animal` ist ein Struct, kein Trait | ^^^^^^ not a traitDas Programm dazu:
struct Animal { name: String,}
struct Dog;
impl Animal for Dog { // ❌ `Animal` ist ein Struct, kein Trait fn name(&self) -> String { String::from("Rex") }}
fn main() { println!("{}", Dog.name());}In Rust ist der gemeinsame Teil ein Trait: eine Liste von Methoden, die jeder Typ liefern muss, plus optionale Standardmethoden. Die Daten bleiben in jedem einzelnen Struct:
trait Animal { // ✅ ein Trait: was jedes Tier kann fn name(&self) -> String;
fn greet(&self) -> String { // eine Standardmethode format!("Hallo, ich bin {}!", self.name()) }}
struct Dog;
struct Cat { name: String, // die Daten liegen im 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());}Ausgabe:
Hallo, ich bin Rex!Hallo, ich bin Tom!Wolltest du nur einem Struct Methoden geben, brauchst du gar keinen Trait: Schreib impl Dog { fn name(&self) -> String { ... } }, ohne for. Siehe Einen Trait definieren.
Lösung 4: Das Struct direkt nehmen oder einen Trait als Bound benutzen
Abschnitt betitelt „Lösung 4: Das Struct direkt nehmen oder einen Trait als Bound benutzen“Nimm diese Lösung, wenn der Fehler auf einen Bound wie T: Shape oder impl Shape zeigt und Shape dein Struct ist:
error[E0404]: expected trait, found struct `Shape` --> src/main.rs:5:16 |5 | fn describe<T: Shape>(item: &T) { // ❌ `Shape` ist ein Struct, kein Trait | ^^^^^ not a traitDas Programm dazu:
struct Shape { sides: u32,}
fn describe<T: Shape>(item: &T) { // ❌ `Shape` ist ein Struct, kein Trait println!("a shape");}
fn main() { describe(&Shape { sides: 3 });}Ein Bound heißt „jeder Typ, der diesen Trait implementiert“. Bekommt die Funktion immer nur ein Shape, lass das Generic weg und nimm &Shape. Soll sie mit vielen Typen funktionieren, pack das gemeinsame Verhalten in einen Trait und benutze ihn als Bound:
struct Shape { sides: u32,}
fn describe(item: &Shape) { // ✅ das Struct selbst nehmen, kein Generic nötig println!("eine Form mit {} Seiten", item.sides);}
trait Named { // ✅ oder ein Trait, für „jeden Typ, der einen Namen hat“ fn name(&self) -> String;}
impl Named for Shape { fn name(&self) -> String { match self.sides { 3 => String::from("Dreieck"), 4 => String::from("Quadrat"), n => format!("{}-Eck", n), } }}
fn shout<T: Named>(item: &T) { // ✅ ein Trait Bound braucht einen Trait println!("{}!", item.name().to_uppercase());}
fn main() { let t = Shape { sides: 3 }; describe(&t); shout(&t); shout(&Shape { sides: 6 });}Ausgabe:
eine Form mit 3 SeitenDREIECK!6-ECK!Trait Bounds werden unter Generics und Traits erklärt.
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“impl Vec<i32> als Parametertyp. fn total(x: impl Vec<i32>) ergibt expected trait, found struct `Vec` . impl braucht an dieser Stelle einen Trait. Lösung: Nimm &[i32] oder Vec<i32>, oder impl IntoIterator<Item = i32>, wenn du jede Sammlung annehmen willst.
Box<dyn String>. Auch dyn braucht einen Trait, also ergibt das expected trait, found struct `String` , und rustc schlägt ToString vor. Lösung: Box<dyn ToString>, wenn du „alles, was zu einer Zeichenkette werden kann“ meinst, oder einfach String. Siehe Trait-Objekte.
Ein Typalias für einen Trait. type Printable = dyn std::fmt::Display; gefolgt von impl Printable for A ergibt expected trait, found type alias `Printable` mit dem Label „type aliases cannot be used as traits“ (Typaliase können nicht als Traits benutzt werden). Lösung: Schreib im impl den echten Namen des Traits.
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0405: cannot find trait in this scope: Den Namen gibt es gar nicht, etwa
Displayohne Import. E0404 heißt dagegen: Der Name existiert, ist aber die falsche Art von Ding. - E0277: the trait bound is not satisfied: Der Trait stimmt, aber der Typ implementiert ihn nicht.
- E0425: cannot find value in this scope: dieselbe Art von „nicht gefunden“-Fehler für Werte, Funktionen und Typen.
- E0107: wrong number of generic arguments: Der Trait oder Typ stimmt, hat aber zu viele oder zu wenige
<...>-Argumente. - E0119: conflicting implementations (auf Englisch):
#[derive(Debug)]und dein eigenesimpl Debugfür denselben Typ. - Alle Compilerfehler: die vollständige Liste der Fehlerseiten.
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. impl Debug for Point ergibt „expected trait, found derive macro Debug“. Was ist die einfachste Lösung, wenn du ein eigenes Format willst?
2. Du hast struct Animal und schreibst impl Animal for Dog. Was will Rust stattdessen?
3. Was davon darf in fn show<T: ___>(x: T) hinter T: stehen?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0404 – Rust error codes index (auf Englisch): die offizielle Erklärung, mit dem Beispiel eines Structs, das als Trait benutzt wird.
- Gemeinsames Verhalten definieren mit Traits – Das Rust-Buch: einen Trait definieren, ihn implementieren und als Bound benutzen.
std::fmt::Debug– Dokumentation der Standardbibliothek (auf Englisch):Debugableiten oder von Hand implementieren.- Traits – Rust by Example (auf Englisch): ein Trait, den sich mehrere Typen teilen, mit Standardmethoden.
std::prelude– Dokumentation der Standardbibliothek (auf Englisch): was in jeder Datei im Gültigkeitsbereich ist, darunter das Derive-MakroDebug, aber nicht der Trait.