Ir al contenido

Error E0034 de Rust: multiple applicable items in scope (método ambiguo)

E0034 significa que tu tipo tiene dos métodos con el mismo nombre, uno de cada uno de dos traits, y lo llamaste de la forma corta, como book.describe(). Rust ve los dos y no va a elegir uno por ti. En tus tipos no hay nada roto: solo tienes que decir a qué trait te refieres. Normalmente basta con una línea, y rustc incluso te la escribe. Forma parte de la guía de errores del compilador.

error[E0034]: multiple applicable items in scope
--> src/main.rs:28:25
|
28 | println!("{}", book.describe()); // ❌ Summary's or Label's?
| ^^^^^^^^ 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()); // ❌ Summary's or Label's?
28 + println!("{}", Label::describe(&book)); // ❌ Summary's or Label's?
|
help: disambiguate the method for candidate #2
|
28 - println!("{}", book.describe()); // ❌ Summary's or Label's?
28 + println!("{}", Summary::describe(&book)); // ❌ Summary's or Label's?
|

“Multiple applicable items in scope” quiere decir “hay varios elementos aplicables en el ámbito”. Las notas candidate #1 y candidate #2 (candidato 1 y candidato 2) dicen en qué impl está cada método, y cada help te enseña cómo deshacer la ambigüedad eligiendo uno de ellos.

Este es el programa que lo produce:

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!("{} ({} pages)", self.title, self.pages)
}
}
impl Label for Book {
fn describe(&self) -> String {
format!("BOOK: {}", self.title.to_uppercase())
}
}
fn main() {
let book = Book { title: String::from("Rust for Crabs"), pages: 320 };
println!("{}", book.describe()); // ❌ Summary's or Label's?
}

Lee el mensaje de arriba abajo:

  1. Los ^^^^^^^^ debajo de describe: “multiple describe found” (se encontraron varios describe). Rust buscó un método llamado describe en Book y encontró más de uno.
  2. Las dos líneas note: enumeran los candidatos: uno viene de impl Label for Book (línea 21) y el otro de impl Summary for Book (línea 15). Los dos son reales, los dos funcionan y devuelven textos distintos.
  3. Las dos líneas help: muestran la solución para cada opción. Summary::describe(&book) significa “llama al describe que viene de Summary, con &book como self”.

Piensa en dos compañeros de trabajo que se llaman Sam y están en la misma oficina. Gritar “¡Sam!” no sirve; dices “Sam, el de contabilidad”. Summary::describe es “Sam, el de contabilidad”.

Una llamada a un método como book.describe() es un atajo. Rust mira el tipo de book y busca entre sus propios métodos y entre los métodos de cada trait que está en el ámbito (in scope) y que Book implementa. Normalmente esa búsqueda encuentra exactamente una coincidencia. Aquí encuentra dos, y hacen cosas distintas: una imprime un resumen y la otra imprime una etiqueta para la estantería.

Rust podría elegir uno con alguna regla oculta, como “el trait que se definió primero”. Pero entonces añadir un trait, o cambiar el orden de dos líneas use, cambiaría sin avisar lo que imprime tu programa. Rust se niega a adivinar y te pide que lo digas una vez, en el código, donde cualquiera que lo lea pueda verlo.

Hay una excepción: un método del bloque propio del tipo, impl Book, siempre gana a un método de un trait con el mismo nombre, así que ese caso nunca da E0034. Puedes leer más en Implementar un trait y en Sintaxis totalmente calificada.

Solución 1: Llámalo a través del trait: Trait::method(&x)

Sección titulada «Solución 1: Llámalo a través del trait: Trait::method(&x)»

Elige esta cuando necesitas los dos métodos y solo quieres decir cuál usas cada vez. Es exactamente lo que sugiere rustc. El valor se pasa como primer argumento, con el mismo & que espera el &self del método:

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!("LIBRO: {}", self.title.to_uppercase())
}
}
fn main() {
let book = Book { title: String::from("Rust para cangrejos"), pages: 320 };
println!("{}", Summary::describe(&book));
println!("{}", Label::describe(&book));
}

Salida:

Rust para cangrejos (320 páginas)
LIBRO: RUST PARA CANGREJOS
▶ Pruébalo tú mismo

Si el método recibe &mut self, pasa &mut book. Si recibe self, pasa book (y se mueve, igual que con el punto).

Solución 2: Sintaxis totalmente calificada: <Type as Trait>::method()

Sección titulada «Solución 2: Sintaxis totalmente calificada: <Type as Trait>::method()»

Elige esta cuando la función que choca no tiene self, como un constructor o un fn category(). Entonces no hay ningún valor que Rust pueda mirar, así que Trait::category() no basta: también tienes que nombrar el tipo. <Book as Summary>::category() se lee “el category que tiene Book porque implementa Summary”. También funciona con métodos:

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("Divulgación") }
fn describe(&self) -> String { format!("Un libro llamado {}", self.title) }
}
impl Label for Book {
fn category() -> String { String::from("Estante B") }
}
fn main() {
let book = Book { title: String::from("Rust para cangrejos") };
println!("{}", <Book as Summary>::category());
println!("{}", <Book as Label>::category());
println!("{}", <Book as Summary>::describe(&book));
}

Salida:

Divulgación
Estante B
Un libro llamado Rust para cangrejos
▶ Pruébalo tú mismo

Aquí, escribir Book::category() también da E0034, con el help use fully-qualified syntax to disambiguate (usa la sintaxis totalmente calificada para deshacer la ambigüedad). Escribir solo Summary::category() da otro error, E0790, porque el trait sigue sin saber a qué tipo te refieres.

Solución 3: Cambia el nombre de uno de los métodos

Sección titulada «Solución 3: Cambia el nombre de uno de los métodos»

Elige esta cuando los dos traits son tuyos y el nombre compartido es una casualidad. Dos métodos que hacen cosas distintas merecen nombres distintos, y entonces el book.describe() corto vuelve a funcionar. A menudo es la solución más limpia:

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!("LIBRO: {}", self.title.to_uppercase()) }
}
fn main() {
let book = Book { title: String::from("Rust para cangrejos"), pages: 320 };
println!("{}", book.describe());
println!("{}", book.label_text());
}

Salida:

Rust para cangrejos (320 páginas)
LIBRO: RUST PARA CANGREJOS
▶ Pruébalo tú mismo

Claro que no puedes renombrar los métodos de traits que vienen de otro crate (paquete). Para esos, usa la Solución 1 o la Solución 4.

Solución 4: Importa solo el trait que necesitas

Sección titulada «Solución 4: Importa solo el trait que necesitas»

Elige esta cuando los traits están en módulos o crates distintos. Los métodos de un trait solo funcionan con la sintaxis del punto mientras el trait está en el ámbito. Así que si una parte de tu código solo necesita uno de ellos, importa solo ese ahí. Incluso puedes poner un use dentro de una función, para que solo se aplique en esa función:

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!("Un libro llamado {}", self.title) }
}
impl shop::Label for Book {
fn describe(&self) -> String { format!("LIBRO: {}", self.title.to_uppercase()) }
}
fn print_label(book: &Book) {
use shop::Label; // aquí solo Label está en el ámbito
println!("{}", book.describe());
}
fn main() {
use report::Summary; // aquí solo Summary está en el ámbito
let book = Book { title: String::from("Rust para cangrejos") };
println!("{}", book.describe());
print_label(&book);
}

Salida:

Un libro llamado Rust para cangrejos
LIBRO: RUST PARA CANGREJOS
▶ Pruébalo tú mismo

Fíjate en que los bloques impl escriben report::Summary y shop::Label completos. Implementar un trait no necesita un use; solo la llamada con punto lo necesita. Mira use: acortar rutas largas para saber más sobre use.

Tu propio to_string(). Todo tipo que implementa Display ya recibe to_string() del trait estándar ToString. Añade un trait tuyo con un método to_string, y point.to_string() da E0034, con candidate #2 is defined in an impl of the trait `ToString` for the type `T` (el candidato 2 está definido en un impl del trait ToString para el tipo T). Solución: cambia el nombre del tuyo (pretty(), to_label()) o llama a Pretty::to_string(&point).

Dos imports prelude::*. Muchas bibliotecas ofrecen use some_crate::prelude::*; para traer sus traits. Importa dos preludes que añadan los dos un método draw() o render() al mismo tipo, y la llamada con punto se vuelve ambigua. Solución: cambia uno de los * por solo los elementos que usas, o llama a Render::draw(&data).

Funciones genéricas con dos bounds. En fn show<T: Summary + Label>(item: &T), item.describe() da E0034. Cuidado aquí con la sugerencia de rustc: te ofrece Label::describe(&item), pero item ya es una referencia, así que eso da E0277 (the trait bound `&T: Label` is not satisfied, no se cumple el trait bound &T: Label). Escribe Label::describe(item) sin el & de más. Mira Genéricos.

  • E0790: llamaste a una función de un trait sin self, como Default::default() o Summary::category(), sin decir qué tipo.
  • E0599: el problema contrario, no se encontró ningún método con ese nombre. A menudo es que el trait no está importado.
  • E0277: no se cumple un trait bound, por ejemplo después de un & de más en Trait::method(&x).
  • E0428: el nombre está definido varias veces. Dos elementos con el mismo nombre en un módulo, en lugar de dos métodos de traits.
  • Todos los errores del compilador: la lista completa de páginas de errores.

Comprueba lo aprendido

1. Book implementa Summary y Label, y los dos tienen fn describe(&self). ¿Qué línea llama a la versión de Label?

2. Los dos traits tienen fn category() -> String sin self. ¿Qué llamada funciona?

3. Book tiene su propio impl Book { fn describe(&self) ... } y además implementa Summary, que también tiene describe. ¿Qué hace book.describe()?

Puntuación: 0 / 3