Pular para o conteúdo

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

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 ^^^^^ 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 é.

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.

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)
▶ Experimente você mesmo

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: 7
▶ Experimente você mesmo

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

O 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!
▶ Experimente você mesmo

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 trait

Mesma 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 lados
TRIÂNGULO!
POLÍGONO DE 6 LADOS!
▶ Experimente você mesmo

Os trait bounds são explicados em Genéricos e Traits.

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.

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