Erro E0034 do Rust: multiple applicable items in scope (método ambíguo)
O E0034 quer dizer que o seu tipo tem dois métodos com o mesmo nome, um de cada um de dois traits, e você chamou o método do jeito curto, como book.describe(). O Rust enxerga os dois e não vai escolher um por você. Não há nada quebrado nos seus tipos; você só precisa dizer de qual trait está falando. Isso normalmente leva uma linha, e o rustc até escreve essa linha para você. Esta página faz parte do guia de erros do compilador.
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0034]: multiple applicable items in scope --> src/main.rs:28:25 |28 | println!("{}", book.describe()); // ❌ o de Summary ou o de Label? | ^^^^^^^^ multiple `describe` found |note: candidate #1 is defined in an impl of the trait `Label` for the type `Book` --> src/main.rs:21:5 |21 | fn describe(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^note: candidate #2 is defined in an impl of the trait `Summary` for the type `Book` --> src/main.rs:15:5 |15 | fn describe(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^help: disambiguate the method for candidate #1 |28 - println!("{}", book.describe()); // ❌ o de Summary ou o de Label?28 + println!("{}", Label::describe(&book)); // ❌ o de Summary ou o de Label? |help: disambiguate the method for candidate #2 |28 - println!("{}", book.describe()); // ❌ o de Summary ou o de Label?28 + println!("{}", Summary::describe(&book)); // ❌ o de Summary ou o de Label? |Em português: “vários itens aplicáveis no escopo”. O rótulo diz “foram encontrados vários describe”, as notas mostram onde cada candidato foi definido, e as duas ajudas dizem “tire a ambiguidade do método para o candidato #1” e “para o candidato #2”.
Este é o programa que gera o erro:
trait Summary { fn describe(&self) -> String;}
trait Label { fn describe(&self) -> String;}
struct Book { title: String, pages: u32,}
impl Summary for Book { fn describe(&self) -> String { format!("{} ({} páginas)", self.title, self.pages) }}
impl Label for Book { fn describe(&self) -> String { format!("LIVRO: {}", self.title.to_uppercase()) }}
fn main() { let book = Book { title: String::from("Rust para Caranguejos"), pages: 320 }; println!("{}", book.describe()); // ❌ o de Summary ou o de Label?}O que significa
Seção intitulada “O que significa”Leia a mensagem de cima para baixo:
^^^^^^^^embaixo dedescribe: “multipledescribefound” (foram encontrados váriosdescribe). O Rust procurou um método chamadodescribeemBooke encontrou mais de um.- As duas linhas
note:listam os candidatos: um vem deimpl Label for Book(linha 21), o outro deimpl Summary for Book(linha 15). Os dois existem de verdade, os dois funcionam, e eles devolvem textos diferentes. - As duas linhas
help:mostram a correção para cada escolha.Summary::describe(&book)quer dizer “chame odescribeque vem deSummary, com&bookcomoself”.
Pense em dois colegas chamados Sam no mesmo escritório. Gritar “Sam!” não funciona; você diz “o Sam do financeiro”. Summary::describe é “o Sam do financeiro”.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”Uma chamada de método como book.describe() é um atalho. O Rust olha o tipo de book e procura entre os métodos próprios dele e os métodos de todo trait que está no escopo e é implementado para Book. Normalmente essa busca encontra exatamente um. Aqui ela encontra dois, e eles fazem coisas diferentes: um mostra um resumo, o outro mostra uma etiqueta de estante.
O Rust poderia escolher um por alguma regra escondida, como “o trait definido primeiro”. Mas aí acrescentar um trait, ou trocar a ordem de duas linhas use, mudaria em silêncio o que o seu programa imprime. O Rust se recusa a chutar e pede que você diga uma vez, no código, onde todo mundo que ler possa ver.
Há uma exceção: um método no bloco impl Book do próprio tipo sempre ganha de um método de trait com o mesmo nome, então esse caso nunca dá E0034. Você pode ler mais em Traits e em Traits avançados.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Chame pelo trait: Trait::metodo(&x)
Seção intitulada “Correção 1: Chame pelo trait: Trait::metodo(&x)”Escolha esta quando você precisa dos dois métodos e só quer dizer qual deles a cada vez. É exatamente o que o rustc sugere. O valor entra como primeiro argumento, com o mesmo & que o &self do método espera:
trait Summary { fn describe(&self) -> String;}
trait Label { fn describe(&self) -> String;}
struct Book { title: String, pages: u32,}
impl Summary for Book { fn describe(&self) -> String { format!("{} ({} páginas)", self.title, self.pages) }}
impl Label for Book { fn describe(&self) -> String { format!("LIVRO: {}", self.title.to_uppercase()) }}
fn main() { let book = Book { title: String::from("Rust para Caranguejos"), pages: 320 }; println!("{}", Summary::describe(&book)); println!("{}", Label::describe(&book));}Saída:
Rust para Caranguejos (320 páginas)LIVRO: RUST PARA CARANGUEJOSSe o método recebe &mut self, passe &mut book. Se recebe self, passe book (e ele é movido, igualzinho a quando você usa o ponto).
Correção 2: Sintaxe totalmente qualificada: <Tipo as Trait>::metodo()
Seção intitulada “Correção 2: Sintaxe totalmente qualificada: <Tipo as Trait>::metodo()”Escolha esta quando a função que se choca não tem self, como um construtor ou um fn category(). Aí não há nenhum valor para o Rust olhar, então Trait::category() não basta: você também precisa dizer o tipo. Leia <Book as Summary>::category() como “o category que Book tem porque implementa Summary”. Isso funciona para métodos também:
trait Summary { fn category() -> String; fn describe(&self) -> String;}
trait Label { fn category() -> String;}
struct Book { title: String,}
impl Summary for Book { fn category() -> String { String::from("Não ficção") } fn describe(&self) -> String { format!("Um livro chamado {}", self.title) }}
impl Label for Book { fn category() -> String { String::from("Estante B") }}
fn main() { let book = Book { title: String::from("Rust para Caranguejos") }; println!("{}", <Book as Summary>::category()); println!("{}", <Book as Label>::category()); println!("{}", <Book as Summary>::describe(&book));}Saída:
Não ficçãoEstante BUm livro chamado Rust para CaranguejosEscrever Book::category() aqui também dá E0034, com a ajuda use fully-qualified syntax to disambiguate (use a sintaxe totalmente qualificada para tirar a ambiguidade). Escrever só Summary::category() dá outro erro, o E0790, porque o trait continua sem saber de qual tipo você está falando.
Correção 3: Renomeie um dos métodos
Seção intitulada “Correção 3: Renomeie um dos métodos”Escolha esta quando os dois traits são seus e o nome repetido foi um acidente. Dois métodos que fazem coisas diferentes merecem nomes diferentes, e aí o book.describe() curto volta a funcionar. Muitas vezes é a correção mais limpa:
trait Summary { fn describe(&self) -> String;}
trait Label { fn label_text(&self) -> String;}
struct Book { title: String, pages: u32,}
impl Summary for Book { fn describe(&self) -> String { format!("{} ({} páginas)", self.title, self.pages) }}
impl Label for Book { fn label_text(&self) -> String { format!("LIVRO: {}", self.title.to_uppercase()) }}
fn main() { let book = Book { title: String::from("Rust para Caranguejos"), pages: 320 }; println!("{}", book.describe()); println!("{}", book.label_text());}Saída:
Rust para Caranguejos (320 páginas)LIVRO: RUST PARA CARANGUEJOSClaro que você não pode renomear métodos de traits de outro crate. Para esses, use a Correção 1 ou a 4.
Correção 4: Importe só o trait de que você precisa
Seção intitulada “Correção 4: Importe só o trait de que você precisa”Escolha esta quando os traits ficam em módulos ou crates diferentes. Os métodos de um trait só funcionam com a sintaxe de ponto enquanto o trait está no escopo. Então, se uma parte do seu código precisa só de um deles, importe só esse ali. Você pode até colocar um use dentro de uma função, e ele só vale dentro dela:
mod report { pub trait Summary { fn describe(&self) -> String; }}
mod shop { pub trait Label { fn describe(&self) -> String; }}
struct Book { title: String,}
impl report::Summary for Book { fn describe(&self) -> String { format!("Um livro chamado {}", self.title) }}
impl shop::Label for Book { fn describe(&self) -> String { format!("LIVRO: {}", self.title.to_uppercase()) }}
fn print_label(book: &Book) { use shop::Label; // aqui só Label está no escopo println!("{}", book.describe());}
fn main() { use report::Summary; // aqui só Summary está no escopo let book = Book { title: String::from("Rust para Caranguejos") }; println!("{}", book.describe()); print_label(&book);}Saída:
Um livro chamado Rust para CaranguejosLIVRO: RUST PARA CARANGUEJOSRepare que os blocos impl escrevem report::Summary e shop::Label por extenso. Implementar um trait não precisa de use; só a chamada com ponto precisa. Veja Módulos para saber mais sobre use.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”O seu próprio to_string(). Todo tipo que implementa Display já ganha to_string() do trait padrão ToString. Acrescente um trait seu com um método to_string, e point.to_string() dá E0034, com candidate #2 is defined in an impl of the trait `ToString` for the type `T` (o candidato #2 está definido num impl do trait ToString). Correção: renomeie o seu (pretty(), to_label()), ou chame Pretty::to_string(&point).
Dois imports prelude::*. Bibliotecas costumam oferecer use algum_crate::prelude::*; para trazer os traits delas. Importe dois preludes que acrescentam um método draw() ou render() ao mesmo tipo, e a chamada com ponto fica ambígua. Correção: troque um dos * só pelos itens que você usa, ou chame Render::draw(&data).
Funções genéricas com dois bounds. Em fn show<T: Summary + Label>(item: &T), item.describe() dá E0034. Cuidado com a sugestão do rustc aqui: ela oferece Label::describe(&item), mas item já é uma referência, então isso dá E0277 (the trait bound `&T: Label` is not satisfied, o trait Label não está implementado para &T). Escreva Label::describe(item), sem o & a mais. Veja Genéricos.
Erros relacionados
Seção intitulada “Erros relacionados”- E0790: você chamou uma função de trait sem
self, comoDefault::default()ouSummary::category(), sem dizer o tipo. - E0599: o problema contrário, nenhum método com esse nome foi encontrado. Muitas vezes o trait não foi importado.
- E0277: trait bound não satisfeito, por exemplo depois de um
&a mais emTrait::metodo(&x). - E0428: o nome foi definido várias vezes. Dois itens com o mesmo nome num módulo, e não dois métodos de trait.
- Todos os erros do compilador: a lista completa de páginas de erro.
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. Book implementa Summary e Label, e os dois têm fn describe(&self). Qual linha chama a versão de Label?
2. Os dois traits têm fn category() -> String, sem self. Qual chamada funciona?
3. Book tem o seu próprio impl Book { fn describe(&self) ... } e também implementa Summary, que também tem describe. O que book.describe() faz?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0034 — Rust error codes index (em inglês): a explicação oficial, com as duas formas totalmente qualificadas.
- Advanced Traits — The Rust Book (em inglês): a seção sobre sintaxe totalmente qualificada, com o exemplo de
Pilot,WizardeHuman. - Disambiguating overlapping traits — Rust by Example (em inglês): um exemplo curto, para rodar, de dois traits com um método de mesmo nome.
- Traits — The Rust Book (em inglês): como definir e implementar traits, e por que um trait precisa estar no escopo para você usar os métodos dele.
- Method call expressions — The Rust Reference (em inglês): as regras exatas que o Rust segue para encontrar o método por trás de
x.metodo().