Zum Inhalt springen

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

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);
}

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.

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.

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 = 1024
2^40 = 1099511627776
sqrt(2) = 1.4142135623730951
|-7| = 7
▶ Selbst ausprobieren

Wä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.

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.414
Fläche = 12.25
5 Grad von null entfernt
1024
▶ Selbst ausprobieren

Ein 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 1
2 zum Quadrat ist 4
3 zum Quadrat ist 9
Summe der Quadrate: 55 und 55
▶ Selbst ausprobieren

Beachte: 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.

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];.

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