Zum Inhalt springen

Rust-Fehler E0034: multiple applicable items in scope (Methode mehrdeutig)

E0034 bedeutet: Dein Typ hat zwei Methoden mit demselben Namen, je eine aus zwei verschiedenen Traits, und du hast sie in der kurzen Form aufgerufen, etwa book.describe(). Rust sieht beide und sucht sich nicht einfach eine aus. An deinen Typen ist nichts kaputt. Du musst nur sagen, welchen Trait du meinst. Meistens ist das eine Zeile, und rustc schreibt sie dir sogar vor. Diese Seite gehört zur Reihe Rust-Compilerfehler einfach erklärt.

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

Auf Deutsch: „Mehrere passende Elemente im Gültigkeitsbereich.“ Die note-Zeilen nennen die beiden Kandidaten, und jedes help zeigt, wie du einen davon eindeutig aufrufst.

Dieses Programm erzeugt den Fehler:

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!("{} ({} Seiten)", self.title, self.pages)
}
}
impl Label for Book {
fn describe(&self) -> String {
format!("BUCH: {}", self.title.to_uppercase())
}
}
fn main() {
let book = Book { title: String::from("Rust für Krabben"), pages: 320 };
println!("{}", book.describe()); // ❌ das von Summary oder das von Label?
}

Lies die Meldung von oben nach unten:

  1. ^^^^^^^^ unter describe: „multiple describe found“ (mehrere describe gefunden). Rust hat bei Book nach einer Methode namens describe gesucht und mehr als eine gefunden.
  2. Die beiden note:-Zeilen nennen die Kandidaten: Einer kommt aus impl Label for Book (Zeile 21), der andere aus impl Summary for Book (Zeile 15). Beide sind echt, beide funktionieren, und sie liefern unterschiedlichen Text.
  3. Die beiden help:-Zeilen zeigen die Lösung für jede Wahl. Summary::describe(&book) heißt: „Ruf das describe auf, das aus Summary kommt, mit &book als self.“

Stell dir zwei Kollegen vor, die beide Sam heißen und im selben Büro sitzen. „Sam!“ zu rufen, funktioniert nicht. Du sagst „Sam aus der Buchhaltung“. Summary::describe ist „Sam aus der Buchhaltung“.

Ein Methodenaufruf wie book.describe() ist eine Abkürzung. Rust schaut sich den Typ von book an und durchsucht dann seine eigenen Methoden und die Methoden jedes Traits, der im Gültigkeitsbereich (Scope) ist und für Book implementiert ist. Normalerweise findet diese Suche genau einen Treffer. Hier findet sie zwei, und die tun Unterschiedliches: Eine gibt eine Zusammenfassung aus, die andere ein Regaletikett.

Rust könnte nach einer versteckten Regel eine auswählen, etwa „der zuerst definierte Trait“. Aber dann würde ein neuer Trait oder das Vertauschen zweier use-Zeilen stillschweigend ändern, was dein Programm ausgibt. Rust weigert sich zu raten und bittet dich, es einmal im Code zu sagen, wo es jeder sehen kann.

Eine Ausnahme gibt es: Eine Methode im eigenen impl Book-Block des Typs gewinnt immer gegen eine Trait-Methode mit demselben Namen. Dieser Fall ergibt also nie E0034. Mehr dazu findest du unter Traits und unter Fortgeschrittene Traits.

Lösung 1: Über den Trait aufrufen: Trait::method(&x)

Abschnitt betitelt „Lösung 1: Über den Trait aufrufen: Trait::method(&x)“

Nimm diese Lösung, wenn du beide Methoden brauchst und einfach jedes Mal sagen willst, welche. Genau das schlägt rustc vor. Der Wert kommt als erstes Argument hinein, mit demselben &, das das &self der Methode erwartet:

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!("{} ({} Seiten)", self.title, self.pages)
}
}
impl Label for Book {
fn describe(&self) -> String {
format!("BUCH: {}", self.title.to_uppercase())
}
}
fn main() {
let book = Book { title: String::from("Rust für Krabben"), pages: 320 };
println!("{}", Summary::describe(&book));
println!("{}", Label::describe(&book));
}

Ausgabe:

Rust für Krabben (320 Seiten)
BUCH: RUST FÜR KRABBEN
▶ Selbst ausprobieren

Nimmt die Methode &mut self, übergib &mut book. Nimmt sie self, übergib book (dann wird es verschoben, genau wie beim Aufruf mit Punkt).

Lösung 2: Vollständig qualifizierte Syntax: <Type as Trait>::method()

Abschnitt betitelt „Lösung 2: Vollständig qualifizierte Syntax: <Type as Trait>::method()“

Nimm diese Lösung, wenn die kollidierende Funktion kein self hat, etwa ein Konstruktor oder ein fn category(). Dann gibt es keinen Wert, den Rust ansehen könnte, und Trait::category() reicht nicht: Du musst auch den Typ nennen. Lies <Book as Summary>::category() als „das category, das Book hat, weil es Summary implementiert“. Das funktioniert auch für Methoden:

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("Sachbuch") }
fn describe(&self) -> String { format!("Ein Buch namens {}", self.title) }
}
impl Label for Book {
fn category() -> String { String::from("Regal B") }
}
fn main() {
let book = Book { title: String::from("Rust für Krabben") };
println!("{}", <Book as Summary>::category());
println!("{}", <Book as Label>::category());
println!("{}", <Book as Summary>::describe(&book));
}

Ausgabe:

Sachbuch
Regal B
Ein Buch namens Rust für Krabben
▶ Selbst ausprobieren

Schreibst du hier Book::category(), bekommst du ebenfalls E0034, mit dem Hinweis use fully-qualified syntax to disambiguate (benutze die vollständig qualifizierte Syntax, um eindeutig zu sein). Schreibst du nur Summary::category(), bekommst du einen anderen Fehler, E0790, denn der Trait weiß immer noch nicht, welchen Typ du meinst.

Nimm diese Lösung, wenn beide Traits dir gehören und der gemeinsame Name ein Zufall ist. Zwei Methoden, die Unterschiedliches tun, verdienen unterschiedliche Namen. Dann funktioniert auch das kurze book.describe() wieder. Oft ist das die sauberste Lösung:

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!("{} ({} Seiten)", self.title, self.pages) }
}
impl Label for Book {
fn label_text(&self) -> String { format!("BUCH: {}", self.title.to_uppercase()) }
}
fn main() {
let book = Book { title: String::from("Rust für Krabben"), pages: 320 };
println!("{}", book.describe());
println!("{}", book.label_text());
}

Ausgabe:

Rust für Krabben (320 Seiten)
BUCH: RUST FÜR KRABBEN
▶ Selbst ausprobieren

Methoden von Traits aus einem fremden Crate kannst du natürlich nicht umbenennen. Dafür nimmst du Lösung 1 oder Lösung 4.

Lösung 4: Nur den Trait importieren, den du brauchst

Abschnitt betitelt „Lösung 4: Nur den Trait importieren, den du brauchst“

Nimm diese Lösung, wenn die Traits in verschiedenen Modulen oder Crates liegen. Die Methoden eines Traits funktionieren mit der Punkt-Syntax nur, solange der Trait im Gültigkeitsbereich ist. Braucht ein Teil deines Codes nur einen davon, importiere dort nur diesen einen. Du kannst ein use sogar in eine Funktion schreiben. Dann gilt es nur in dieser Funktion:

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!("Ein Buch namens {}", self.title) }
}
impl shop::Label for Book {
fn describe(&self) -> String { format!("BUCH: {}", self.title.to_uppercase()) }
}
fn print_label(book: &Book) {
use shop::Label; // hier ist nur Label im Gültigkeitsbereich
println!("{}", book.describe());
}
fn main() {
use report::Summary; // hier ist nur Summary im Gültigkeitsbereich
let book = Book { title: String::from("Rust für Krabben") };
println!("{}", book.describe());
print_label(&book);
}

Ausgabe:

Ein Buch namens Rust für Krabben
BUCH: RUST FÜR KRABBEN
▶ Selbst ausprobieren

Beachte, dass die impl-Blöcke report::Summary und shop::Label ausschreiben. Um einen Trait zu implementieren, brauchst du kein use, nur für den Aufruf mit Punkt. Mehr über use findest du unter Module.

Dein eigenes to_string(). Jeder Typ, der Display implementiert, bekommt to_string() schon vom Standard-Trait ToString. Schreibst du einen eigenen Trait mit einer Methode to_string, ergibt point.to_string() E0034, mit candidate #2 is defined in an impl of the trait `ToString` for the type `T` . Lösung: Benenn deine Methode um (pretty(), to_label()), oder ruf Pretty::to_string(&point) auf.

Zwei prelude::*-Importe. Bibliotheken bieten oft use some_crate::prelude::*; an, um ihre Traits einzubinden. Importierst du zwei Preludes, die demselben Typ beide eine Methode draw() oder render() geben, wird der Aufruf mit Punkt mehrdeutig. Lösung: Ersetze einen der Glob-Importe durch genau die Elemente, die du benutzt, oder ruf Render::draw(&data) auf.

Generische Funktionen mit zwei Bounds. In fn show<T: Summary + Label>(item: &T) ergibt item.describe() E0034. Vorsicht mit dem Vorschlag von rustc: Er bietet Label::describe(&item) an, aber item ist schon eine Referenz. Das ergibt dann E0277 (the trait bound `&T: Label` is not satisfied, &T implementiert Label nicht). Schreib Label::describe(item) ohne das zusätzliche &. Siehe Generics.

  • E0790: Du hast eine Trait-Funktion ohne self aufgerufen, etwa Default::default() oder Summary::category(), ohne den Typ zu nennen.
  • E0599: das umgekehrte Problem – keine Methode mit diesem Namen gefunden. Oft ist der Trait nicht importiert.
  • E0277: Trait-Bound nicht erfüllt, zum Beispiel nach einem zusätzlichen & in Trait::method(&x).
  • E0428: Der Name ist mehrfach definiert. Hier sind es zwei Elemente mit demselben Namen in einem Modul, nicht zwei Trait-Methoden.
  • Alle Compilerfehler: die vollständige Liste der Fehlerseiten.

Kurzer Check

1. Book implementiert Summary und Label, und beide haben fn describe(&self). Welche Zeile ruft die Version von Label auf?

2. Beide Traits haben fn category() -> String ohne self. Welcher Aufruf funktioniert?

3. Book hat ein eigenes impl Book { fn describe(&self) ... } und implementiert außerdem Summary, das auch describe hat. Was macht book.describe()?

Punkte: 0 / 3