Erro E0404 do Rust: expected trait, found struct ou derive macro (não é trait)
O E0404 quer dizer: o Rust encontrou o nome que você escreveu, mas ele não é um trait, e só um trait pode ir naquele lugar. Esses lugares são impl ___ for Tipo, um bound (restrição) como T: ___, e dyn ___ ou impl ___. O caso mais comum pega muita gente de surpresa: impl Debug for Point sem import. O Debug está em escopo, mas como a macro #[derive(Debug)], não como o trait. O outro caso comum é uma struct usada como se fosse um trait, o que acontece bastante com quem está acostumado com classes e herança. Os dois se corrigem rápido. (Esta página faz parte de Erros do compilador Rust explicados.)
A mensagem de erro
Seção intitulada “A mensagem de erro”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; |Em português: “era esperado um trait, mas foi encontrada a macro derive Debug”. O rótulo diz “not a trait” (não é um trait), e a ajuda sugere importar o trait com use std::fmt::Debug;.
Este é o programa que gera o erro:
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);}(O comentário quer dizer “aqui Debug é a macro derive, não o trait”. Ele fica em inglês porque aparece na saída do compilador.)
O que significa
Seção intitulada “O que significa”O ^^^^^ aponta para Debug na linha 6, com o rótulo “not a trait”. A primeira linha diz o que o Rust encontrou no lugar: uma macro derive chamada Debug.
Isso acontece porque existem duas coisas chamadas Debug. A macro Debug, que você usa como #[derive(Debug)], está no prelude, então todo arquivo enxerga ela. O trait std::fmt::Debug, de que você precisa para impl Debug for ..., não está no prelude. Então o Rust encontra o único Debug que consegue ver, percebe que é uma macro e para. A linha de ajuda traz o import que traz o trait. O Hash funciona do mesmo jeito: expected trait, found derive macro `Hash` , que se corrige com use std::hash::Hash;.
Quando a mensagem diz found struct `Animal` (ou enum, ou type alias), o nome é um dos seus próprios tipos, usado onde deveria ir um trait.
É como pedir “um motorista habilitado” e receber um carro. O carro é uma coisa real, e bem relacionada, mas o trabalho pedia alguém que sabe dirigir: um conjunto de habilidades, que é exatamente o que um trait é.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”O Rust separa dados de comportamento. Uma struct ou um enum descreve dados: o que um valor é. Um trait descreve comportamento: o que um tipo sabe fazer. impl Trait for Tipo liga os dois: “este tipo sabe fazer estas coisas”. Um bound como T: Trait quer dizer “qualquer tipo que saiba fazer estas coisas”.
Uma struct não pode ser usada nesses lugares, porque ela não é uma lista de habilidades. E o Rust não tem herança, então impl Animal for Dog não pode querer dizer “Dog é um tipo de Animal” quando Animal é uma struct. Se você quer que vários tipos compartilhem um comportamento, a parte compartilhada é um trait. Veja Traits e Genéricos.
O Rust também guarda macros e traits em namespaces (espaços de nomes) separados, e é por isso que Debug pode ser ao mesmo tempo uma macro e um trait. A macro derive está no prelude para que #[derive(Debug)] funcione em qualquer lugar; o trait só precisa ser citado quando você mesmo o implementa ou o usa num bound.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Importe o trait (use std::fmt::Debug;)
Seção intitulada “Correção 1: Importe o trait (use std::fmt::Debug;)”Escolha esta quando a mensagem diz found derive macro e você quer um formato próprio, ou precisa do trait num bound como T: Debug. Acrescente a linha da ajuda:
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 em ({}, {})", 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 }]);}Saída:
Point em (3, 4)Point em (0, 0)Point em (-1, 7)Uma alternativa comum é use std::fmt; e depois impl fmt::Debug for Point, com fmt::Formatter e fmt::Result. Os dois jeitos estão certos; escolha um estilo por arquivo.
Correção 2: Use #[derive(Debug)] em vez de escrever o impl
Seção intitulada “Correção 2: Use #[derive(Debug)] em vez de escrever o impl”Escolha esta quando você não precisa de um formato próprio, o que no caso do Debug é quase sempre. A macro derive escreve o impl Debug for Point inteiro para você, e você não precisa de import nenhum:
#[derive(Debug)] // ✅ a macro derive escreve `impl Debug for Point` para vocêstruct Point { x: i32, y: i32,}
fn main() { let p = Point { x: 3, y: 4 }; println!("{:?}", p); println!("{:#?}", p); println!("distância de (0, 0) andando na grade: {}", p.x.abs() + p.y.abs());}Saída:
Point { x: 3, y: 4 }Point { x: 3, y: 4,}distância de (0, 0) andando na grade: 7Use um ou outro, não os dois: #[derive(Debug)] junto com o seu próprio impl Debug dá um erro de “conflicting implementations” (implementações conflitantes, E0119, em inglês). O {:#?} é a forma bonita, em várias linhas. Mais em Traits comuns.
Correção 3: Defina um trait para o comportamento compartilhado
Seção intitulada “Correção 3: Defina um trait para o comportamento compartilhado”Escolha esta quando a mensagem diz found struct numa linha impl X for Y. Provavelmente você queria que Dog fosse “um tipo de Animal”, como uma subclasse:
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 traitO rustc diz que esperava um trait, mas encontrou a struct Animal.
O programa por trás dele:
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());}Em Rust, a parte compartilhada é um trait: uma lista de métodos que cada tipo precisa fornecer, mais métodos padrão opcionais. Os dados ficam em cada struct:
trait Animal { // ✅ um trait: o que todo animal sabe fazer fn name(&self) -> String;
fn greet(&self) -> String { // um método padrão format!("Oi, eu sou o {}!", self.name()) }}
struct Dog;
struct Cat { name: String, // os dados moram na 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());}Saída:
Oi, eu sou o Rex!Oi, eu sou o Tom!Se você só queria acrescentar métodos a uma struct, nem precisa de trait: escreva impl Dog { fn name(&self) -> String { ... } }, sem for. Veja Definindo um trait.
Correção 4: Receba a própria struct, ou use um trait no bound
Seção intitulada “Correção 4: Receba a própria struct, ou use um trait no bound”Escolha esta quando o erro aponta para um bound como T: Shape ou impl Shape, e Shape é a sua 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 traitMesma mensagem: o bound pede um trait, e Shape é uma struct.
O programa por trás dele:
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 });}Um bound quer dizer “qualquer tipo que implemente este trait”. Se a função só recebe Shape, tire o genérico e receba &Shape. Se ela deve funcionar com muitos tipos, coloque o comportamento compartilhado num trait e use esse trait no bound:
struct Shape { sides: u32,}
fn describe(item: &Shape) { // ✅ recebe a própria struct, sem genérico println!("uma forma com {} lados", item.sides);}
trait Named { // ✅ ou um trait, para "qualquer tipo que tenha um nome" fn name(&self) -> String;}
impl Named for Shape { fn name(&self) -> String { match self.sides { 3 => String::from("triângulo"), 4 => String::from("quadrado"), n => format!("polígono de {} lados", n), } }}
fn shout<T: Named>(item: &T) { // ✅ um trait bound precisa de um trait println!("{}!", item.name().to_uppercase());}
fn main() { let t = Shape { sides: 3 }; describe(&t); shout(&t); shout(&Shape { sides: 6 });}Saída:
uma forma com 3 ladosTRIÂNGULO!POLÍGONO DE 6 LADOS!Os trait bounds são explicados em Genéricos e Traits.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”impl Vec<i32> como tipo de parâmetro. fn total(x: impl Vec<i32>) dá expected trait, found struct `Vec` . O impl nesse lugar precisa de um trait. Correção: receba &[i32] ou Vec<i32>, ou impl IntoIterator<Item = i32> se você quer aceitar qualquer coleção.
Box<dyn String>. O dyn também precisa de um trait, então isso dá expected trait, found struct `String` , e o rustc sugere ToString. Correção: Box<dyn ToString> se você queria dizer “qualquer coisa que vire uma string”, ou simplesmente String. Veja Trait objects.
Um type alias para um trait. type Printable = dyn std::fmt::Display; seguido de impl Printable for A dá expected trait, found type alias `Printable` , com o rótulo “type aliases cannot be used as traits” (type aliases não podem ser usados como traits). Correção: escreva o nome verdadeiro do trait no impl.
Erros relacionados
Seção intitulada “Erros relacionados”- E0405: cannot find trait in this scope: o nome nem existe, como
Displaysem import. O E0404 quer dizer que o nome existe, mas é do tipo errado. - E0277: the trait bound is not satisfied: o trait está certo, mas o tipo não o implementa.
- E0425: cannot find value in this scope: o mesmo tipo de erro “não encontrado” para valores, funções e tipos.
- E0107: wrong number of generic arguments: o trait ou o tipo está certo, mas tem argumentos
<...>demais ou de menos. - E0119: conflicting implementations (em inglês):
#[derive(Debug)]e o seu próprioimpl Debugno mesmo tipo. - Todos os erros do compilador explicados
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. impl Debug for Point dá "expected trait, found derive macro Debug". Qual é a correção mais simples se você quer um formato próprio?
2. Você tem struct Animal e escreve impl Animal for Dog. O que o Rust quer no lugar disso?
3. Qual destes pode vir depois de T: em fn show<T: ___>(x: T)?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0404 — Rust error codes index (em inglês): a explicação oficial, com o exemplo da struct usada como trait.
- Defining Shared Behavior with Traits — The Rust Book (em inglês): como definir um trait, implementá-lo e usá-lo como bound.
std::fmt::Debug— documentação da biblioteca padrão (em inglês): derivarDebugou implementá-lo à mão.- Traits — Rust by Example (em inglês): um trait compartilhado por vários tipos, com métodos padrão.
std::prelude— documentação da biblioteca padrão (em inglês): o que está em escopo em todo arquivo, incluindo a macro deriveDebug, mas não o trait.