Rust-Fehler E0689: can't call method on ambiguous numeric type (unklarer Typ)
E0689 bedeutet: Du hast eine Methode auf einer Zahl aufgerufen, aber Rust weiß noch nicht, welche Art von Zahl das ist. 2 könnte ein i32 sein, ein u8, ein u64 und noch einiges mehr, und jeder dieser Typen hat seine eigene Methode pow. Du musst Rust nur sagen, welche du meinst – meistens mit ein paar Zeichen mehr, etwa 2_i32 oder let x: f64. rustc zeigt dir sogar, wo sie hingehören. (Diese Seite gehört zur Reihe Rust-Compilerfehler einfach erklärt.)
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}` --> src/main.rs:2:16 |2 | let kb = 2.pow(10); // ❌ welche Art von Ganzzahl ist `2`? | ^^^ |help: you must specify a concrete type for this numeric value, like `i32` |2 | let kb = 2_i32.pow(10); // ❌ welche Art von Ganzzahl ist `2`? | ++++Auf Deutsch: „Die Methode pow lässt sich nicht auf dem mehrdeutigen Zahlentyp {integer} aufrufen.“ Das help sagt: „Du musst für diesen Zahlenwert einen konkreten Typ angeben, etwa i32.“
Dieses Programm erzeugt den Fehler:
fn main() { let kb = 2.pow(10); // ❌ welche Art von Ganzzahl ist `2`? println!("{}", kb);}Bei Kommazahlen passiert dasselbe. Hier steht die Zahl zuerst in einer Variable, deshalb schlägt rustc stattdessen einen Typ für die Variable vor:
error[E0689]: can't call method `sqrt` on ambiguous numeric type `{float}` --> src/main.rs:3:18 |3 | let root = x.sqrt(); // ❌ ist `x` ein f32 oder ein f64? | ^^^^ |help: you must specify a type for this binding, like `f32` |2 | let x: f32 = 2.0; | +++++Das help heißt hier: „Du musst für diese Variable einen Typ angeben, etwa f32.“
fn main() { let x = 2.0; let root = x.sqrt(); // ❌ ist `x` ein f32 oder ein f64? println!("{}", root);}Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“Mit {integer} und {float} sagt rustc: „eine Zahl, deren genauen Typ ich noch nicht festgelegt habe“. {integer} ist irgendein Ganzzahltyp (i8 bis i128, u8 bis u128, isize, usize). {float} ist f32 oder f64.
Die ^^^ zeigen auf den Namen der Methode. Eine Methode gehört zu einem Typ: Es gibt ein i32::pow, ein u64::pow, ein f32::sqrt, ein f64::sqrt und so weiter. Sie liefern unterschiedliche Ergebnisse (ein i32 kann 2 hoch 40 nicht speichern, ein u64 schon). Um eine davon aufzurufen, muss Rust den Typ an dieser Stelle im Code kennen, und hier kennt es ihn nicht.
Das ist, als würdest du im Laden fragen: „Was kosten 2?“ Zwei was? Zwei Äpfel und zwei Fernseher kosten sehr unterschiedlich viel. Sobald du „2 Äpfel“ sagst, ist die Antwort einfach.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“Rust hat durchaus Standardtypen für Zahlen. Legt nichts anderes den Typ fest, wird eine Ganzzahl zu i32 und eine Kommazahl zu f64. Siehe Ganzzahlen (Integer) und Gleitkommazahlen (Kommazahlen).
Dieser Standard ist aber nur der letzte Ausweg. Rust liest zuerst den Rest der Funktion, um zu sehen, ob etwas anderes den Typ festlegt – zum Beispiel, wenn du die Zahl an eine Funktion übergibst, die ein u8 erwartet. Erst ganz am Ende greift Rust auf i32 oder f64 zurück. Ein Methodenaufruf kann nicht so lange warten: Rust muss wissen, welches pow es aufruft, bevor es die Argumente und den Ergebnistyp prüfen kann. Statt zu raten, hält Rust also an und fragt dich.
Das heißt auch: Späterer Code hilft nicht. Selbst let y: u64 = x.pow(10); ergibt E0689, denn Rust muss den Typ von x vor dem Aufruf von pow festlegen, und das u64 steht nur am Ergebnis. Was hilft: der Zahl ihren Typ vor dem Methodenaufruf geben.
So behebst du den Fehler
Abschnitt betitelt „So behebst du den Fehler“Lösung 1: Der Zahl ein Typ-Suffix geben
Abschnitt betitelt „Lösung 1: Der Zahl ein Typ-Suffix geben“Nimm diese Lösung, wenn du eine Methode direkt auf einem Literal aufrufst, etwa 2.pow(10) oder 2.0.sqrt(). Schreib den Typ direkt hinter die Zahl. Der _ ist optional und dient nur der Lesbarkeit: 2_i32 und 2i32 sind dasselbe.
Für Kommazahlen schlägt rustc f32 vor, aber f64 ist der Standard in Rust und genauer. Nimm f64, außer du hast einen Grund, Speicher zu sparen.
fn main() { let kb = 2_i32.pow(10); // ✅ ein i32 let big = 2_u64.pow(40); // ✅ ein u64: 2^40 passt nicht in einen i32 let root = 2.0_f64.sqrt(); // ✅ ein f64 let dist = (-7_i32).abs(); // ✅ Klammern, weil das `-` vor dem Methodenaufruf kommt
println!("2^10 = {}", kb); println!("2^40 = {}", big); println!("sqrt(2) = {}", root); println!("|-7| = {}", dist);}Ausgabe:
2^10 = 10242^40 = 1099511627776sqrt(2) = 1.4142135623730951|-7| = 7Wähle einen Typ, der groß genug für das Ergebnis ist. 2_i32.pow(40) kompiliert zwar, läuft aber über und löst in einem Debug-Build eine Panic aus. Siehe Integer-Überlauf.
Lösung 2: Der Variable einen Typ geben
Abschnitt betitelt „Lösung 2: Der Variable einen Typ geben“Nimm diese Lösung, wenn die Zahl zuerst in einer Variable steht, etwa let x = 2.0;. Genau das schlägt die help:-Zeile von rustc vor. Schreib : Typ hinter den Variablennamen:
fn main() { let x: f64 = 2.0; // ✅ jetzt ist x ein f64 println!("{:.3}", x.sqrt());
let side: f64 = 3.5; println!("Fläche = {}", side.powi(2));
let temperature: i32 = -5; println!("{} Grad von null entfernt", temperature.abs());
let base: u64 = 2; let y = base.pow(10); // y ist auch ein u64 println!("{}", y);}Ausgabe:
1.414Fläche = 12.255 Grad von null entfernt1024Ein Suffix am Wert funktioniert genauso gut: let x = 2.0_f64;. Nimm, was du besser lesen kannst. Siehe Typannotationen.
Lösung 3: Der Schleifenvariable oder dem Closure-Argument einen Typ geben
Abschnitt betitelt „Lösung 3: Der Schleifenvariable oder dem Closure-Argument einen Typ geben“Nimm diese Lösung, wenn die Zahl aus einem Bereich (Range) kommt, etwa for i in 1..=3 oder (1..=5).map(|n| ...). Die Variable i oder n hat den Typ {integer}, und es gibt kein let, das du annotieren könntest. rustc zeigt auf die Variable:
error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}` --> src/main.rs:3:26 |2 | for i in 1..=3 { | - you must specify a type for this binding, like `i32`3 | println!("{}", i.pow(2)); | ^^^Schreib das Suffix an ein Ende des Bereichs, oder gib dem Closure-Argument einen Typ:
fn main() { for i in 1..=3_u32 { // ✅ der ganze Bereich ist jetzt u32 println!("{} zum Quadrat ist {}", i, i.pow(2)); }
// entweder ein Suffix am Bereich ... let total: u32 = (1..=5_u32).map(|n| n.pow(2)).sum(); // ... oder ein Typ am Closure-Argument let total2: u32 = (1..=5).map(|n: u32| n.pow(2)).sum(); println!("Summe der Quadrate: {} und {}", total, total2);}Ausgabe:
1 zum Quadrat ist 12 zum Quadrat ist 43 zum Quadrat ist 9Summe der Quadrate: 55 und 55Beachte: let total: u32 = ... allein reicht nicht. rustc braucht den Typ von n im Closure, bevor es bei .sum() ankommt. Ein einziges Suffix am Bereich behebt alles.
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“abs() auf einer negativen Zahl. let x = -5; x.abs() ergibt can't call method `abs` on ambiguous numeric type `{integer}`. Lösung: let x: i32 = -5;. Vorsicht mit einem Suffix am Literal: -5_i32.abs() bedeutet -(5_i32.abs()), und das ist -5. Benutze Klammern: (-5_i32).abs().
Schnelle Rechnungen in main oder in einem Test. let area = 3.14 * r * r; funktioniert problemlos, denn * ist ein Operator, keine Methode. Der Fehler taucht erst auf, wenn du eine Methode wie .sqrt(), .round() oder .powi() aufrufst. Lösung: let r: f64 = 2.0;.
Ein Vec mit Zahlen. Bei let nums = vec![1, 2, 3]; ergibt nums.iter().map(|n| n.pow(2)) einen anderen Code, E0599, no method named `pow` found for reference `&{integer}` (keine Methode pow für die Referenz &{integer}), denn n ist hier eine Referenz. Die Lösung ist dieselbe: vec![1_i32, 2, 3] oder let nums: Vec<i32> = vec![1, 2, 3];.
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0282: type annotations needed: Rust kann einen Typ überhaupt nicht ableiten, zum Beispiel bei
Vec::new()oder.collect(). Die Lösung folgt derselben Idee: Nenn den Typ. - E0610: primitive type doesn’t have fields:
.somethingohne()auf einem{integer}, etwax.absstattx.abs(). - E0599: no method named x found: Der Typ ist bekannt, hat aber keine solche Methode – oder (wie oben) es ist eine Referenz auf ein
{integer}. - E0308: mismatched types: Das siehst du vielleicht als Nächstes, wenn der gewählte Typ nicht zu der Stelle passt, an der die Zahl benutzt wird.
- Alle Compilerfehler einfach erklärt
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. Welche Zeile kompiliert?
2. Welchen Typ gibt Rust 7 und 7.0, wenn nichts anderes den Typ festlegt?
3. for i in 0..10 { println!("{}", i.pow(2)); } ergibt E0689. Was ist die einfachste Lösung?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0689 – Rust error codes index (auf Englisch): die offizielle Erklärung dieses Fehlers.
- Datentypen – Das Rust-Buch: Ganzzahl- und Fließkomma-Typen, Zahlenliterale mit Suffixen wie
57u8und die Standardtypeni32/f64. - Literals – Rust by Example (auf Englisch): wie Suffixe wie
1u32und1.0f64einen Typ festlegen. - Integer literal expressions – The Rust Reference (auf Englisch): die genauen Regeln, wie ein Literal seinen Typ bekommt, einschließlich des Rückgriffs auf
i32. f64::sqrt– Doku der Standardbibliothek (auf Englisch): die Methoden für Kommazahlen (sqrt,powi,round…), die einen bekannten Typ brauchen.