Error E0689 de Rust: can't call method on ambiguous numeric type (tipo numérico)
E0689 significa: llamaste a un método sobre un número, pero Rust todavía no sabe qué clase de número es. 2 podría ser un i32, un u8, un u64 y más, y cada uno de ellos tiene su propio método pow. Solo tienes que decirle a Rust cuál quieres, normalmente con unos pocos caracteres más, como 2_i32 o let x: f64. rustc incluso te enseña dónde ponerlos. (Esta página forma parte de Errores del compilador de Rust explicados.)
El mensaje de error
Sección titulada «El mensaje de error»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`? | ++++“Can’t call method pow on ambiguous numeric type {integer}” quiere decir “no se puede llamar al método pow sobre el tipo numérico ambiguo {integer}”. El help te pide que indiques un tipo concreto para este número, por ejemplo i32, y te enseña dónde escribirlo.
Este es el programa que lo produce:
fn main() { let kb = 2.pow(10); // ❌ which kind of integer is `2`? println!("{}", kb);}Con los decimales pasa lo mismo. Aquí el número se guarda antes en una variable, así que rustc te sugiere en su lugar un tipo para la variable:
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; | +++++El help dice que tienes que indicar un tipo para esta variable, por ejemplo f32.
fn main() { let x = 2.0; let root = x.sqrt(); // ❌ is `x` an f32 or an f64? println!("{}", root);}Qué significa
Sección titulada «Qué significa»{integer} y {float} son la forma que tiene rustc de decir “un número cuyo tipo exacto todavía no he elegido”. {integer} es algún tipo de número entero (de i8 a i128, de u8 a u128, isize, usize). {float} es f32 o f64.
Los ^^^ señalan el nombre del método. Un método pertenece a un tipo: hay un i32::pow, un u64::pow, un f32::sqrt, un f64::sqrt, y así. Dan resultados distintos (un i32 no puede guardar 2 elevado a 40, un u64 sí). Para llamar a uno, Rust tiene que conocer el tipo en ese punto del código, y aquí no lo conoce.
Es como preguntarle a alguien en una tienda “¿cuánto cuestan 2?”. ¿Dos qué? Dos manzanas y dos televisores cuestan cantidades muy distintas. En cuanto dices “2 manzanas”, la respuesta es fácil.
Por qué Rust hace esto
Sección titulada «Por qué Rust hace esto»Rust sí tiene tipos numéricos por defecto. Si nada más lo decide, un número entero pasa a ser un i32 y un decimal pasa a ser un f64. Mira Enteros (números sin decimales) y Números de coma flotante (decimales).
Pero ese valor por defecto es un último recurso. Rust primero lee el resto de la función para ver si otra cosa elige el tipo, por ejemplo pasar el número a una función que recibe un u8. Solo al final del todo recurre a i32 o f64. Una llamada a un método no puede esperar tanto: Rust necesita saber qué pow está llamando antes de poder comprobar los argumentos y el tipo del resultado. Así que, en lugar de adivinar, se detiene y te pregunta.
Esto también significa que el código que viene después no ayuda. Incluso let y: u64 = x.pow(10); da E0689, porque Rust tiene que elegir el tipo de x antes de llamar a pow, y el u64 solo está en el resultado. Lo que ayuda es darle su tipo al número antes de la llamada al método.
Cómo solucionarlo
Sección titulada «Cómo solucionarlo»Solución 1: Añade un sufijo de tipo al número
Sección titulada «Solución 1: Añade un sufijo de tipo al número»Elige esta cuando llamas a un método directamente sobre un literal, como 2.pow(10) o 2.0.sqrt(). Escribe el tipo justo después del número. El _ es opcional y solo sirve para que se lea mejor: 2_i32 y 2i32 son lo mismo.
Para los decimales, rustc sugiere f32, pero f64 es el tipo por defecto de Rust y es más preciso. Elige f64 a menos que tengas un motivo para ahorrar memoria.
fn main() { let kb = 2_i32.pow(10); // ✅ un i32 let big = 2_u64.pow(40); // ✅ un u64: 2^40 no cabe en un i32 let root = 2.0_f64.sqrt(); // ✅ un f64 let dist = (-7_i32).abs(); // ✅ con paréntesis, porque el `-` va antes de la llamada al método
println!("2^10 = {}", kb); println!("2^40 = {}", big); println!("raíz(2) = {}", root); println!("|-7| = {}", dist);}Salida:
2^10 = 10242^40 = 1099511627776raíz(2) = 1.4142135623730951|-7| = 7Elige un tipo lo bastante grande para el resultado. 2_i32.pow(40) compila, pero se desborda y provoca un panic en una compilación de depuración (debug). Mira Desbordamiento de enteros.
Solución 2: Dale un tipo a la variable
Sección titulada «Solución 2: Dale un tipo a la variable»Elige esta cuando el número se guarda antes en una variable, como let x = 2.0;. Es lo que sugiere la línea help: de rustc. Añade : tipo después del nombre de la variable:
fn main() { let x: f64 = 2.0; // ✅ ahora x es un f64 println!("{:.3}", x.sqrt());
let side: f64 = 3.5; println!("área = {}", side.powi(2));
let temperature: i32 = -5; println!("{} grados desde cero", temperature.abs());
let base: u64 = 2; let y = base.pow(10); // y también es un u64 println!("{}", y);}Salida:
1.414área = 12.255 grados desde cero1024Un sufijo en el valor funciona igual de bien: let x = 2.0_f64;. Usa el que te resulte más fácil de leer. Mira Anotaciones de tipo.
Solución 3: Dale un tipo a la variable del bucle o al argumento de la closure
Sección titulada «Solución 3: Dale un tipo a la variable del bucle o al argumento de la closure»Elige esta cuando el número viene de un rango, como for i in 1..=3 o (1..=5).map(|n| ...). La variable i o n tiene el tipo {integer}, y no hay ningún let que anotar. rustc señala la 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)); | ^^^La etiqueta junto a i dice que tienes que indicar un tipo para esa variable, por ejemplo i32. Pon el sufijo en uno de los extremos del rango, o dale un tipo al argumento de la closure (función anónima):
fn main() { for i in 1..=3_u32 { // ✅ ahora todo el rango es u32 println!("{} al cuadrado es {}", i, i.pow(2)); }
// o un sufijo en el rango... let total: u32 = (1..=5_u32).map(|n| n.pow(2)).sum(); // ...o un tipo en el argumento de la closure let total2: u32 = (1..=5).map(|n: u32| n.pow(2)).sum(); println!("suma de cuadrados: {} y {}", total, total2);}Salida:
1 al cuadrado es 12 al cuadrado es 43 al cuadrado es 9suma de cuadrados: 55 y 55Fíjate en que let total: u32 = ... por sí solo no basta: rustc necesita el tipo de n dentro de la closure, antes de llegar a .sum(). Un solo sufijo en el rango lo arregla todo.
Dónde lo verás normalmente
Sección titulada «Dónde lo verás normalmente»abs() sobre un número negativo. let x = -5; x.abs() da can't call method `abs` on ambiguous numeric type `{integer}`. Solución: let x: i32 = -5;. Cuidado con poner el sufijo en el literal: -5_i32.abs() significa -(5_i32.abs()), que es -5. Usa paréntesis: (-5_i32).abs().
Cálculos rápidos en main o en un test. let area = 3.14 * r * r; funciona bien, porque * es un operador, no un método. El error solo aparece cuando llamas a un método como .sqrt(), .round() o .powi(). Solución: let r: f64 = 2.0;.
Un Vec de números. Con let nums = vec![1, 2, 3];, nums.iter().map(|n| n.pow(2)) da otro código, E0599, no method named `pow` found for reference `&{integer}` (no se encontró ningún método llamado pow para la referencia &{integer}), porque aquí n es una referencia. La solución es la misma: vec![1_i32, 2, 3] o let nums: Vec<i32> = vec![1, 2, 3];.
Errores relacionados
Sección titulada «Errores relacionados»- E0282: type annotations needed: Rust no puede deducir un tipo en absoluto, por ejemplo con
Vec::new()o.collect(). La solución sigue la misma idea: di el tipo. - E0610: primitive type doesn’t have fields:
.algosin()sobre un{integer}, comox.absen lugar dex.abs(). - E0599: no method named x found: el tipo se conoce, pero no tiene ese método o (como arriba) es una referencia a un
{integer}. - E0308: mismatched types: lo que podrías ver después, si el tipo que eliges no coincide con el lugar donde se usa el número.
- Todos los errores del compilador: la lista completa de páginas de errores.
Comprueba lo que aprendiste
Sección titulada «Comprueba lo que aprendiste»Comprueba lo aprendido
1. ¿Qué línea compila?
2. Si nada más lo decide, ¿qué tipo les da Rust a 7 y a 7.0?
3. for i in 0..10 { println!("{}", i.pow(2)); } da E0689. ¿Cuál es la solución más sencilla?
Puntuación: 0 / 3
Para leer más
Sección titulada «Para leer más»- E0689 — Rust error codes index (en inglés): la explicación oficial de este error.
- Data Types — El libro de Rust (en inglés): los tipos enteros y de coma flotante, los literales numéricos con sufijos como
57u8, y los tipos por defectoi32/f64. También existe una traducción al español de la comunidad del libro. - Literals — Rust by Example (en inglés): cómo los sufijos como
1u32y1.0f64eligen un tipo. - Integer literal expressions — The Rust Reference (en inglés): las reglas exactas de cómo un literal recibe su tipo, incluido el
i32por defecto. f64::sqrt— documentación de la biblioteca estándar (en inglés): los métodos de coma flotante (sqrt,powi,round…) que necesitan un tipo conocido.