Ir al contenido

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

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

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

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.

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 = 1024
2^40 = 1099511627776
raíz(2) = 1.4142135623730951
|-7| = 7
▶ Pruébalo tú mismo

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

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.25
5 grados desde cero
1024
▶ Pruébalo tú mismo

Un 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 1
2 al cuadrado es 4
3 al cuadrado es 9
suma de cuadrados: 55 y 55
▶ Pruébalo tú mismo

Fí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.

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

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