Errore Rust E0689: can't call method on ambiguous numeric type (numero ambiguo)
E0689 significa: hai chiamato un metodo su un numero, ma Rust non sa ancora di che tipo di numero si tratta. 2 potrebbe essere un i32, un u8, un u64 e altro ancora, e ognuno di questi ha il suo metodo pow. Devi solo dire a Rust quale intendi, di solito con pochi caratteri in più come 2_i32 o let x: f64. rustc ti mostra persino dove metterli. (Questa pagina fa parte di Errori del compilatore Rust spiegati.)
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}` --> src/main.rs:2:16 |2 | let kb = 2.pow(10); // ❌ which kind of integer is `2`? | ^^^ |help: you must specify a concrete type for this numeric value, like `i32` |2 | let kb = 2_i32.pow(10); // ❌ which kind of integer is `2`? | ++++Il compilatore ti dice: «non si può chiamare il metodo pow sul tipo numerico ambiguo {integer}», e ti suggerisce di specificare un tipo concreto, come i32.
Ecco il programma che lo provoca:
fn main() { let kb = 2.pow(10); // ❌ which kind of integer is `2`? println!("{}", kb);}(Il commento dice: che tipo di intero è 2?)
Lo stesso succede con i decimali. Qui il numero viene prima salvato in una variabile, quindi rustc suggerisce invece un tipo per la variabile:
error[E0689]: can't call method `sqrt` on ambiguous numeric type `{float}` --> src/main.rs:3:18 |3 | let root = x.sqrt(); // ❌ is `x` an f32 or an f64? | ^^^^ |help: you must specify a type for this binding, like `f32` |2 | let x: f32 = 2.0; | +++++fn main() { let x = 2.0; let root = x.sqrt(); // ❌ is `x` an f32 or an f64? println!("{}", root);}(Il commento dice: x è un f32 o un f64?)
Cosa significa
Sezione intitolata “Cosa significa”{integer} e {float} sono il modo in cui rustc dice «un numero di cui non ho ancora scelto il tipo esatto». {integer} è un qualche tipo intero (da i8 a i128, da u8 a u128, isize, usize). {float} è f32 oppure f64.
I ^^^ indicano il nome del metodo. Un metodo appartiene a un tipo: esiste un i32::pow, un u64::pow, un f32::sqrt, un f64::sqrt, e così via. Danno risultati diversi (un i32 non può contenere 2 alla 40, un u64 sì). Per chiamarne uno, Rust deve conoscere il tipo in quel punto del codice, e qui non lo conosce.
È come chiedere a un commesso «quanto costano 2?». Due cosa? Due mele e due televisori costano cifre molto diverse. Appena dici «2 mele», la risposta è facile.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”Rust ha dei tipi numerici predefiniti. Se nient’altro decide, un numero intero diventa un i32 e un decimale diventa un f64. Vedi Tipi di dati.
Ma quel valore predefinito è l’ultima risorsa. Rust prima legge il resto della funzione per vedere se qualcos’altro sceglie il tipo, per esempio passando il numero a una funzione che riceve un u8. Solo alla fine ripiega su i32 o f64. Una chiamata di metodo non può aspettare così tanto: Rust deve sapere quale pow sta chiamando prima di poter controllare gli argomenti e il tipo del risultato. Quindi, invece di indovinare, si ferma e te lo chiede.
Questo vuol dire anche che il codice successivo non aiuta. Persino let y: u64 = x.pow(10); dà E0689, perché Rust deve scegliere il tipo di x prima di chiamare pow, e l’u64 sta solo sul risultato. Quello che aiuta è dare un tipo al numero prima della chiamata di metodo.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: aggiungi un suffisso di tipo al numero
Sezione intitolata “Soluzione 1: aggiungi un suffisso di tipo al numero”Scegli questa strada quando chiami un metodo direttamente su un letterale, come 2.pow(10) o 2.0.sqrt(). Scrivi il tipo subito dopo il numero. Il _ è facoltativo e serve solo a rendere il tutto leggibile: 2_i32 e 2i32 sono la stessa cosa.
Per i decimali rustc suggerisce f32, ma f64 è il tipo predefinito di Rust ed è più preciso. Scegli f64 a meno che tu non abbia un motivo per risparmiare memoria.
fn main() { let kb = 2_i32.pow(10); // ✅ un i32 let big = 2_u64.pow(40); // ✅ un u64: 2^40 non sta in un i32 let root = 2.0_f64.sqrt(); // ✅ un f64 let dist = (-7_i32).abs(); // ✅ parentesi, perché il `-` viene prima della chiamata di metodo
println!("2^10 = {}", kb); println!("2^40 = {}", big); println!("radice di 2 = {}", root); println!("|-7| = {}", dist);}Output:
2^10 = 10242^40 = 1099511627776radice di 2 = 1.4142135623730951|-7| = 7Scegli un tipo abbastanza grande per il risultato. 2_i32.pow(40) compila, ma va in overflow e fa panic in una build di debug. Vedi Overflow degli interi.
Soluzione 2: dai un tipo alla variabile
Sezione intitolata “Soluzione 2: dai un tipo alla variabile”Scegli questa strada quando il numero viene prima salvato in una variabile, come let x = 2.0;. È quello che suggerisce la riga help: di rustc. Aggiungi : tipo dopo il nome della variabile:
fn main() { let x: f64 = 2.0; // ✅ ora x è un f64 println!("{:.3}", x.sqrt());
let side: f64 = 3.5; println!("area = {}", side.powi(2));
let temperature: i32 = -5; println!("{} gradi da zero", temperature.abs());
let base: u64 = 2; let y = base.pow(10); // anche y è un u64 println!("{}", y);}Output:
1.414area = 12.255 gradi da zero1024Anche un suffisso sul valore funziona allo stesso modo: let x = 2.0_f64;. Usa quello che trovi più leggibile. Vedi Tipi di dati.
Soluzione 3: dai un tipo alla variabile del ciclo o al parametro della closure
Sezione intitolata “Soluzione 3: dai un tipo alla variabile del ciclo o al parametro della closure”Scegli questa strada quando il numero arriva da un intervallo (range), come for i in 1..=3 o (1..=5).map(|n| ...). La variabile i o n ha tipo {integer}, e non c’è nessun let da annotare. rustc indica la variabile:
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)); | ^^^Metti il suffisso su un estremo dell’intervallo, oppure dai un tipo al parametro della closure:
fn main() { for i in 1..=3_u32 { // ✅ ora tutto l'intervallo è u32 println!("{} al quadrato fa {}", i, i.pow(2)); }
// un suffisso sull'intervallo... let total: u32 = (1..=5_u32).map(|n| n.pow(2)).sum(); // ...oppure un tipo sul parametro della closure let total2: u32 = (1..=5).map(|n: u32| n.pow(2)).sum(); println!("somma dei quadrati: {} e {}", total, total2);}Output:
1 al quadrato fa 12 al quadrato fa 43 al quadrato fa 9somma dei quadrati: 55 e 55Nota che let total: u32 = ... da solo non basta: rustc ha bisogno del tipo di n dentro la closure, prima di arrivare a .sum(). Un solo suffisso sull’intervallo sistema tutto.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”abs() su un numero negativo. let x = -5; x.abs() dà can't call method `abs` on ambiguous numeric type `{integer}`. Soluzione: let x: i32 = -5;. Attenzione al suffisso sul letterale: -5_i32.abs() vuol dire -(5_i32.abs()), cioè -5. Usa le parentesi: (-5_i32).abs().
Calcoli veloci in main o in un test. let area = 3.14 * r * r; funziona benissimo, perché * è un operatore, non un metodo. L’errore compare solo quando chiami un metodo come .sqrt(), .round() o .powi(). Soluzione: let r: f64 = 2.0;.
Un Vec di numeri. Con let nums = vec![1, 2, 3];, nums.iter().map(|n| n.pow(2)) dà un codice diverso, E0599, no method named `pow` found for reference `&{integer}`, perché qui n è un riferimento. Stessa soluzione: vec![1_i32, 2, 3] oppure let nums: Vec<i32> = vec![1, 2, 3];.
Errori correlati
Sezione intitolata “Errori correlati”- E0282: type annotations needed: Rust non riesce proprio a dedurre un tipo, per esempio per
Vec::new()o.collect(). La soluzione segue la stessa idea: nomina il tipo. - E0610: primitive type doesn’t have fields:
.qualcosasenza()su un{integer}, comex.absinvece dix.abs(). - E0599: no method named x found: il tipo è noto, ma non ha quel metodo, oppure (come sopra) è un riferimento a un
{integer}. - E0308: mismatched types: quello che potresti vedere dopo, se il tipo che scegli non corrisponde a dove viene usato il numero.
- Tutti gli errori del compilatore spiegati
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. Quale riga compila?
2. Se nient'altro decide, che tipo dà Rust a 7 e a 7.0?
3. for i in 0..10 { println!("{}", i.pow(2)); } dà E0689. Qual è la soluzione più semplice?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0689 — Rust error codes index (in inglese): la spiegazione ufficiale di questo errore.
- Data Types — The Rust Book (in inglese): tipi interi e float, letterali numerici con suffissi come
57u8, e i tipi predefinitii32/f64. - Literals — Rust by Example (in inglese): come i suffissi come
1u32e1.0f64scelgono un tipo. - Integer literal expressions — The Rust Reference (in inglese): le regole esatte con cui un letterale ottiene il suo tipo, compreso il ripiego su
i32. f64::sqrt— libreria standard di Rust (in inglese): i metodi dei float (sqrt,powi,round…) che hanno bisogno di un tipo noto.