Salta ai contenuti

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.)

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);
}

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.

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.

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)
▶ Provalo tu

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 tuo
struct 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: 7
▶ Provalo tu

Usa 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 trait

Il 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!
▶ Provalo tu

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 trait

Il 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 lati
TRIANGOLO!
POLIGONO DI 6 LATI!
▶ Provalo tu

I vincoli di trait sono spiegati in Generici in Rust e Trait in Rust.

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.

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