Pular para o conteúdo

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.

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?
}

Leia a mensagem de cima para baixo:

  1. ^^^^^^^^ embaixo de describe: “multiple describe found” (foram encontrados vários describe). O Rust procurou um método chamado describe em Book e encontrou mais de um.
  2. As duas linhas note: listam os candidatos: um vem de impl Label for Book (linha 21), o outro de impl Summary for Book (linha 15). Os dois existem de verdade, os dois funcionam, e eles devolvem textos diferentes.
  3. As duas linhas help: mostram a correção para cada escolha. Summary::describe(&book) quer dizer “chame o describe que vem de Summary, com &book como self”.

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

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.

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

Se 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ção
Estante B
Um livro chamado Rust para Caranguejos
▶ Experimente você mesmo

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

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

Claro 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 Caranguejos
LIVRO: RUST PARA CARANGUEJOS
▶ Experimente você mesmo

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

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.

  • E0790: você chamou uma função de trait sem self, como Default::default() ou Summary::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 em Trait::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

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