Errore Rust E0034: multiple applicable items in scope (metodo ambiguo)
E0034 significa che il tuo tipo ha due metodi con lo stesso nome, uno per ciascuno di due trait, e tu l’hai chiamato nel modo breve, come book.describe(). Rust li vede entrambi e non ne sceglie uno al posto tuo. Nei tuoi tipi non c’è niente di rotto; devi solo dire quale trait intendi. Di solito basta una riga, e rustc te la scrive persino. Questa pagina fa parte della guida agli errori del compilatore.
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”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? |Il compilatore ti dice: «più elementi applicabili in scope». Ha trovato più di un describe, ti mostra i due candidati e, per ciascuno, la riga che toglie l’ambiguità.
Ecco il programma che lo provoca:
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?}(Il commento dice: quello di Summary o quello di Label?)
Cosa significa
Sezione intitolata “Cosa significa”Leggi il messaggio dall’alto in basso:
^^^^^^^^sottodescribe: «multipledescribefound». Rust ha cercato un metodo chiamatodescribesuBooke ne ha trovato più di uno.- Le due righe
note:elencano i candidati: uno viene daimpl Label for Book(riga 21), l’altro daimpl Summary for Book(riga 15). Sono veri tutti e due, funzionano tutti e due, e restituiscono testi diversi. - Le due righe
help:mostrano la soluzione per ciascuna scelta.Summary::describe(&book)vuol dire «chiama ildescribeche viene daSummary, con&bookcomeself».
Pensa a due colleghi che si chiamano entrambi Sara nello stesso ufficio. Gridare «Sara!» non funziona; dici «Sara dell’amministrazione». Summary::describe è «Sara dell’amministrazione».
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”Una chiamata di metodo come book.describe() è una scorciatoia. Rust guarda il tipo di book, poi cerca tra i suoi metodi e tra i metodi di ogni trait che è in scope e implementato per Book. Di solito quella ricerca trova esattamente una corrispondenza. Qui ne trova due, e fanno cose diverse: uno stampa un riassunto, l’altro stampa un’etichetta da scaffale.
Rust potrebbe sceglierne uno secondo una regola nascosta, come «il trait definito per primo». Ma allora aggiungere un trait, o invertire due righe use, cambierebbe in silenzio quello che stampa il programma. Rust si rifiuta di indovinare e ti chiede di dirlo una volta, nel codice, dove ogni lettore può vederlo.
C’è un’eccezione: un metodo nel blocco proprio del tipo, impl Book, vince sempre su un metodo di trait con lo stesso nome, quindi quel caso non dà mai E0034. Trovi di più in Trait e in Trait avanzati.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: chiamalo attraverso il trait: Trait::method(&x)
Sezione intitolata “Soluzione 1: chiamalo attraverso il trait: Trait::method(&x)”Scegli questa strada quando ti servono entrambi i metodi e vuoi solo dire ogni volta quale. È esattamente ciò che suggerisce rustc. Il valore entra come primo argomento, con lo stesso & che si aspetta il &self del metodo:
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!("{} ({} pagine)", 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 per granchi"), pages: 320 }; println!("{}", Summary::describe(&book)); println!("{}", Label::describe(&book));}Output:
Rust per granchi (320 pagine)LIBRO: RUST PER GRANCHISe il metodo riceve &mut self, passa &mut book. Se riceve self, passa book (e viene spostato, proprio come con il punto).
Soluzione 2: la sintassi completa: <Tipo as Trait>::method()
Sezione intitolata “Soluzione 2: la sintassi completa: <Tipo as Trait>::method()”Scegli questa strada quando la funzione in conflitto non ha self, come un costruttore o una fn category(). In quel caso Rust non ha nessun valore da guardare, quindi Trait::category() non basta: devi nominare anche il tipo. <Book as Summary>::category() si legge «la category che Book ha perché implementa Summary». Funziona anche per i metodi:
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("Saggistica") } fn describe(&self) -> String { format!("Un libro intitolato {}", self.title) }}
impl Label for Book { fn category() -> String { String::from("Scaffale B") }}
fn main() { let book = Book { title: String::from("Rust per granchi") }; println!("{}", <Book as Summary>::category()); println!("{}", <Book as Label>::category()); println!("{}", <Book as Summary>::describe(&book));}Output:
SaggisticaScaffale BUn libro intitolato Rust per granchiAnche scrivere Book::category() qui dà E0034, con l’help use fully-qualified syntax to disambiguate. Scrivere solo Summary::category() dà un errore diverso, E0790, perché il trait non sa comunque quale tipo intendi.
Soluzione 3: rinomina uno dei metodi
Sezione intitolata “Soluzione 3: rinomina uno dei metodi”Scegli questa strada quando entrambi i trait sono tuoi e il nome in comune è un caso. Due metodi che fanno cose diverse meritano nomi diversi, e allora il breve book.describe() torna a funzionare. Spesso è la soluzione più pulita:
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!("{} ({} pagine)", 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 per granchi"), pages: 320 }; println!("{}", book.describe()); println!("{}", book.label_text());}Output:
Rust per granchi (320 pagine)LIBRO: RUST PER GRANCHIOvviamente non puoi rinominare i metodi dei trait di un altro crate. Per quelli, usa la Soluzione 1 o la 4.
Soluzione 4: importa solo il trait che ti serve
Sezione intitolata “Soluzione 4: importa solo il trait che ti serve”Scegli questa strada quando i trait vivono in moduli o crate diversi. I metodi di un trait funzionano con la sintassi del punto solo finché il trait è in scope. Quindi, se una parte del tuo codice ha bisogno di uno solo dei due, importa solo quello. Puoi persino mettere un use dentro una funzione, così vale solo lì:
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 intitolato {}", 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; // qui è in scope solo Label println!("{}", book.describe());}
fn main() { use report::Summary; // qui è in scope solo Summary let book = Book { title: String::from("Rust per granchi") }; println!("{}", book.describe()); print_label(&book);}Output:
Un libro intitolato Rust per granchiLIBRO: RUST PER GRANCHINota che i blocchi impl scrivono report::Summary e shop::Label per intero. Per implementare un trait non serve un use; serve solo per la chiamata con il punto. Vedi Moduli per saperne di più su use.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”Un tuo to_string(). Ogni tipo che implementa Display riceve già to_string() dal trait standard ToString. Aggiungi un tuo trait con un metodo to_string, e point.to_string() dà E0034, con candidate #2 is defined in an impl of the trait `ToString` for the type `T` . Soluzione: rinomina il tuo (pretty(), to_label()), oppure chiama Pretty::to_string(&point).
Due import prelude::*. Le librerie offrono spesso use some_crate::prelude::*; per portare in scope i loro trait. Importa due prelude che aggiungono entrambi un metodo draw() o render() allo stesso tipo, e la chiamata con il punto diventa ambigua. Soluzione: sostituisci uno dei due import con asterisco con i soli elementi che usi, oppure chiama Render::draw(&data).
Funzioni generiche con due vincoli. In fn show<T: Summary + Label>(item: &T), item.describe() dà E0034. Attenzione al suggerimento di rustc in questo caso: propone Label::describe(&item), ma item è già un riferimento, quindi così ottieni E0277 (the trait bound `&T: Label` is not satisfied). Scrivi Label::describe(item) senza il & in più. Vedi Generici.
Errori correlati
Sezione intitolata “Errori correlati”- E0790: hai chiamato una funzione di trait senza
self, comeDefault::default()oSummary::category(), senza dire quale tipo. - E0599: il problema opposto, nessun metodo con quel nome è stato trovato. Spesso il trait non è importato.
- E0277: vincolo di trait non soddisfatto, per esempio dopo un
&di troppo inTrait::method(&x). - E0428: il nome è definito più volte. Due elementi con lo stesso nome in un modulo, invece di due metodi di trait.
- Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. Book implementa Summary e Label, ed entrambi hanno fn describe(&self). Quale riga chiama la versione di Label?
2. Entrambi i trait hanno fn category() -> String senza self. Quale chiamata funziona?
3. Book ha un suo impl Book { fn describe(&self) ... } e implementa anche Summary, che ha anch'esso describe. Cosa fa book.describe()?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0034 — Rust error codes index (in inglese): la spiegazione ufficiale, con entrambe le forme complete.
- Advanced Traits — The Rust Book (in inglese): la sezione sulla sintassi completa, con l’esempio di
Pilot,WizardeHuman. - Disambiguating overlapping traits — Rust by Example (in inglese): un breve esempio eseguibile di due trait con un metodo dello stesso nome.
- Traits — The Rust Book (in inglese): definire e implementare trait, e perché un trait deve essere in scope per usarne i metodi.
- Method call expressions — The Rust Reference (in inglese): le regole esatte che Rust segue per trovare il metodo dietro
x.method().