Errore Rust E0404: expected trait, found struct or derive macro (non è un trait)
E0404 significa: Rust ha trovato il nome che hai scritto, ma non è un trait, e in quel punto può stare solo un trait. I punti sono impl ___ for Tipo, un vincolo come T: ___, e dyn ___ o impl ___. Il caso più comune sorprende molte persone: impl Debug for Point senza import. Debug è in scope, ma come macro #[derive(Debug)], non come trait. L’altro caso comune è una struct usata come se fosse un trait, cosa che succede spesso se sei abituato a classi ed ereditarietà. Entrambi si correggono in fretta. (Questa pagina fa parte di Errori del compilatore Rust spiegati.)
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”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; |Il compilatore dice: si aspettava un trait, e invece ha trovato la derive macro Debug. Questo è il programma che produce l’errore:
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);}Cosa significa
Sezione intitolata “Cosa significa”Il ^^^^^ indica Debug alla riga 6, con l’etichetta «not a trait» (non è un trait). La prima riga dice che cosa ha trovato Rust al suo posto: una derive macro chiamata Debug.
Succede perché esistono due cose che si chiamano Debug. La macro Debug, che usi come #[derive(Debug)], sta nel prelude, quindi ogni file la vede. Il trait std::fmt::Debug, che ti serve per impl Debug for ..., non sta nel prelude. Così Rust trova l’unico Debug che vede, si accorge che è una macro e si ferma. La riga di help ti dà l’import che porta il trait. Hash funziona allo stesso modo: expected trait, found derive macro `Hash` , che si risolve con use std::hash::Hash;.
Quando la prima riga dice found struct `Animal` (oppure enum, o type alias), il nome è uno dei tuoi tipi, usato dove ci vorrebbe un trait.
È come chiedere «un autista con patente» e ricevere un’auto. L’auto è una cosa vera, e molto vicina all’argomento, ma il lavoro chiedeva qualcuno che sappia guidare: un insieme di capacità, che è proprio ciò che è un trait.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”Rust separa i dati dal comportamento. Una struct o un enum descrive dati: che cosa è un valore. Un trait descrive un comportamento: che cosa sa fare un tipo. impl Trait for Tipo collega le due cose: «questo tipo sa fare queste cose». Un vincolo come T: Trait significa «qualsiasi tipo che sappia fare queste cose».
Una struct non può stare in quei punti, perché non è una lista di capacità. E Rust non ha l’ereditarietà, quindi impl Animal for Dog non può significare «Dog è un tipo di Animal» quando Animal è una struct. Se vuoi che più tipi condividano un comportamento, la parte condivisa è un trait. Vedi Trait in Rust e Generici in Rust.
Rust tiene anche macro e trait in namespace separati, ed è per questo che Debug può essere sia una macro sia un trait. La derive macro sta nel prelude così #[derive(Debug)] funziona ovunque; il trait va nominato solo quando lo implementi tu o lo usi come vincolo.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: importa il trait (use std::fmt::Debug;)
Sezione intitolata “Soluzione 1: importa il trait (use std::fmt::Debug;)”Scegli questa quando il messaggio dice found derive macro e vuoi un formato tutto tuo, o quando ti serve il trait in un vincolo come T: Debug. Aggiungi la riga dell’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, "Punto in ({}, {})", 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:
Punto in (3, 4)Punto in (0, 0)Punto in (-1, 7)Un’alternativa comune è use std::fmt; e poi impl fmt::Debug for Point con fmt::Formatter e fmt::Result. Vanno bene entrambe; scegli uno stile per file.
Soluzione 2: usa #[derive(Debug)] invece di scrivere l’impl
Sezione intitolata “Soluzione 2: usa #[derive(Debug)] invece di scrivere l’impl”Scegli questa quando non ti serve un formato personalizzato, cioè quasi sempre per Debug. La derive macro scrive per te tutto l’impl Debug for Point, e non ti serve nessun import:
#[derive(Debug)] // ✅ la derive macro scrive `impl Debug for Point` al posto tuostruct Point { x: i32, y: i32,}
fn main() { let p = Point { x: 3, y: 4 }; println!("{:?}", p); println!("{:#?}", p); println!("distanza da (0, 0) sulla griglia: {}", p.x.abs() + p.y.abs());}Output:
Point { x: 3, y: 4 }Point { x: 3, y: 4,}distanza da (0, 0) sulla griglia: 7Usa l’uno o l’altro, non entrambi: #[derive(Debug)] più un tuo impl Debug danno un errore «conflicting implementations» (E0119, in inglese). {:#?} è la forma «pretty», su più righe. Di più in Trait comuni in Rust.
Soluzione 3: definisci un trait per il comportamento condiviso
Sezione intitolata “Soluzione 3: definisci un trait per il comportamento condiviso”Scegli questa quando il messaggio dice found struct in una riga impl X for Y. Probabilmente volevi che Dog fosse «un tipo di Animal», come una sottoclasse:
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 traitIl programma che lo produce:
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 la parte condivisa è un trait: una lista di metodi che ogni tipo deve fornire, più eventuali metodi predefiniti. I dati restano in ogni struct:
trait Animal { // ✅ un trait: ciò che ogni animale sa fare fn name(&self) -> String;
fn greet(&self) -> String { // un metodo predefinito format!("Ciao, sono {}!", self.name()) }}
struct Dog;
struct Cat { name: String, // i dati vivono nella 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:
Ciao, sono Rex!Ciao, sono Tom!Se volevi solo aggiungere metodi a una struct, non ti serve proprio un trait: scrivi impl Dog { fn name(&self) -> String { ... } }, senza for. Vedi Trait in Rust.
Soluzione 4: prendi direttamente la struct, oppure usa un vincolo su un trait
Sezione intitolata “Soluzione 4: prendi direttamente la struct, oppure usa un vincolo su un trait”Scegli questa quando l’errore indica un vincolo come T: Shape o impl Shape, e Shape è la tua 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 traitIl programma che lo produce:
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 });}Un vincolo dice «qualsiasi tipo che implementi questo trait». Se la funzione riceve sempre e solo una Shape, togli il generico e prendi &Shape. Se deve funzionare con molti tipi, metti il comportamento condiviso in un trait e usa quello come vincolo:
struct Shape { sides: u32,}
fn describe(item: &Shape) { // ✅ prendi la struct stessa, niente generico println!("una forma con {} lati", item.sides);}
trait Named { // ✅ oppure un trait, per «qualsiasi tipo che ha un nome» fn name(&self) -> String;}
impl Named for Shape { fn name(&self) -> String { match self.sides { 3 => String::from("triangolo"), 4 => String::from("quadrato"), n => format!("poligono di {} lati", n), } }}
fn shout<T: Named>(item: &T) { // ✅ un vincolo di trait ha bisogno di un trait println!("{}!", item.name().to_uppercase());}
fn main() { let t = Shape { sides: 3 }; describe(&t); shout(&t); shout(&Shape { sides: 6 });}Output:
una forma con 3 latiTRIANGOLO!POLIGONO DI 6 LATI!I vincoli di trait sono spiegati in Generici in Rust e Trait in Rust.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”impl Vec<i32> come tipo di un parametro. fn total(x: impl Vec<i32>) dà expected trait, found struct `Vec` . In quel punto impl ha bisogno di un trait. Correzione: prendi &[i32] o Vec<i32>, oppure impl IntoIterator<Item = i32> se vuoi accettare qualsiasi collezione.
Box<dyn String>. Anche dyn ha bisogno di un trait, quindi questo dà expected trait, found struct `String` , e rustc suggerisce ToString. Correzione: Box<dyn ToString> se intendevi «qualsiasi cosa che può diventare una stringa», oppure semplicemente String. Vedi Trait object in Rust.
Un alias di tipo per un trait. type Printable = dyn std::fmt::Display; seguito da impl Printable for A dà expected trait, found type alias `Printable` con l’etichetta «type aliases cannot be used as traits» (gli alias di tipo non si possono usare come trait). Correzione: scrivi nell’impl il vero nome del trait.
Errori correlati
Sezione intitolata “Errori correlati”- E0405: cannot find trait in this scope: il nome non esiste proprio, come
Displaysenza import. E0404 significa che il nome esiste ma è del tipo sbagliato. - E0277: the trait bound is not satisfied: il trait è giusto, ma il tipo non lo implementa.
- E0425: cannot find value in this scope: lo stesso genere di errore «non trovato» per valori, funzioni e tipi.
- E0107: wrong number of generic arguments: il trait o il tipo è giusto, ma ha troppi o troppo pochi argomenti
<...>. - E0119: conflicting implementations (in inglese):
#[derive(Debug)]e un tuoimpl Debugsullo stesso tipo. - Tutti gli errori del compilatore spiegati
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. impl Debug for Point dà "expected trait, found derive macro Debug". Qual è la correzione più semplice se vuoi un formato personalizzato?
2. Hai struct Animal e scrivi impl Animal for Dog. Che cosa vuole Rust invece?
3. Quale di questi può stare dopo T: in fn show<T: ___>(x: T)?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0404 — Rust error codes index (in inglese): la spiegazione ufficiale, con l’esempio della struct usata come trait.
- Defining Shared Behavior with Traits — The Rust Book (in inglese): definire un trait, implementarlo e usarlo come vincolo.
std::fmt::Debug— documentazione della libreria standard (in inglese): derivareDebugoppure implementarlo a mano.- Traits — Rust by Example (in inglese): un trait condiviso da più tipi, con metodi predefiniti.
std::prelude— documentazione della libreria standard (in inglese): che cosa è in scope in ogni file, compresa la derive macroDebugma non il trait.