Pular para o conteúdo

Erro E0689 do Rust: can't call method on ambiguous numeric type (tipo numérico)

O E0689 quer dizer: você chamou um método num número, mas o Rust ainda não sabe que tipo de número ele é. 2 pode ser um i32, um u8, um u64 e outros, e cada um desses tem o seu próprio método pow. Você só precisa dizer ao Rust qual deles quer, normalmente com uns poucos caracteres a mais, como 2_i32 ou let x: f64. O rustc até mostra onde colocá-los. (Esta página faz parte de Erros do compilador 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); // ❌ que tipo de inteiro é o `2`?
| ^^^
|
help: you must specify a concrete type for this numeric value, like `i32`
|
2 | let kb = 2_i32.pow(10); // ❌ que tipo de inteiro é o `2`?
| ++++

Em português: “não é possível chamar o método pow no tipo numérico ambíguo {integer}”. A ajuda diz “você precisa especificar um tipo concreto para este valor numérico, como i32”, e mostra onde escrever o sufixo.

Este é o programa que gera o erro:

fn main() {
let kb = 2.pow(10); // ❌ que tipo de inteiro é o `2`?
println!("{}", kb);
}

O mesmo acontece com decimais. Aqui o número é guardado numa variável antes, então o rustc sugere um tipo para a variável:

error[E0689]: can't call method `sqrt` on ambiguous numeric type `{float}`
--> src/main.rs:3:18
|
3 | let root = x.sqrt(); // ❌ `x` é um f32 ou um f64?
| ^^^^
|
help: you must specify a type for this binding, like `f32`
|
2 | let x: f32 = 2.0;
| +++++

A ajuda diz “você precisa especificar um tipo para esta variável, como f32”.

fn main() {
let x = 2.0;
let root = x.sqrt(); // ❌ `x` é um f32 ou um f64?
println!("{}", root);
}

{integer} e {float} são o jeito do rustc dizer “um número cujo tipo exato eu ainda não escolhi”. {integer} é algum tipo de número inteiro (de i8 a i128, de u8 a u128, isize, usize). {float} é f32 ou f64.

O ^^^ aponta para o nome do método. Um método pertence a um tipo: existe um i32::pow, um u64::pow, um f32::sqrt, um f64::sqrt, e assim por diante. Eles dão resultados diferentes (um i32 não consegue guardar 2 elevado a 40, um u64 consegue). Para chamar um deles, o Rust precisa saber o tipo naquele ponto do código, e aqui ele não sabe.

É como perguntar numa loja “quanto custam 2?”. Dois o quê? Duas maçãs e duas TVs custam valores bem diferentes. Quando você diz “2 maçãs”, a resposta fica fácil.

O Rust tem, sim, tipos numéricos padrão. Se nada mais decidir, um número inteiro vira i32 e um decimal vira f64. Veja Inteiros (números inteiros) e Números de ponto flutuante (decimais).

Mas esse padrão é o último recurso. Primeiro o Rust lê o resto da função para ver se alguma outra coisa escolhe o tipo, por exemplo passar o número para uma função que recebe um u8. Só bem no final ele cai no i32 ou no f64. Uma chamada de método não pode esperar tanto: o Rust precisa saber qual pow está chamando antes de conferir os argumentos e o tipo do resultado. Então, em vez de chutar, ele para e pergunta para você.

Isso também quer dizer que o código que vem depois não ajuda. Até let y: u64 = x.pow(10); dá E0689, porque o Rust precisa escolher o tipo de x antes de chamar pow, e o u64 está só no resultado. O que ajuda é dar o tipo ao número antes da chamada do método.

Correção 1: Acrescente um sufixo de tipo ao número

Seção intitulada “Correção 1: Acrescente um sufixo de tipo ao número”

Escolha esta quando você chama um método direto num número escrito no código, como 2.pow(10) ou 2.0.sqrt(). Escreva o tipo logo depois do número. O _ é opcional e só serve para facilitar a leitura: 2_i32 e 2i32 são a mesma coisa.

Para decimais, o rustc sugere f32, mas f64 é o padrão do Rust e é mais preciso. Prefira f64, a não ser que você tenha um motivo para economizar memória.

fn main() {
let kb = 2_i32.pow(10); // ✅ um i32
let big = 2_u64.pow(40); // ✅ um u64: 2^40 não cabe num i32
let root = 2.0_f64.sqrt(); // ✅ um f64
let dist = (-7_i32).abs(); // ✅ parênteses, porque o `-` vem antes da chamada do método
println!("2^10 = {}", kb);
println!("2^40 = {}", big);
println!("sqrt(2) = {}", root);
println!("|-7| = {}", dist);
}

Saída:

2^10 = 1024
2^40 = 1099511627776
sqrt(2) = 1.4142135623730951
|-7| = 7
▶ Experimente você mesmo

Escolha um tipo grande o bastante para o resultado. 2_i32.pow(40) compila, mas estoura e dá panic num build de debug. Veja Integer overflow.

Escolha esta quando o número é guardado numa variável antes, como let x = 2.0;. É isso que a linha help: do rustc sugere. Acrescente : tipo depois do nome da variável:

fn main() {
let x: f64 = 2.0; // ✅ agora x é um f64
println!("{:.3}", x.sqrt());
let side: f64 = 3.5;
println!("área = {}", side.powi(2));
let temperature: i32 = -5;
println!("{} graus de distância do zero", temperature.abs());
let base: u64 = 2;
let y = base.pow(10); // y também é um u64
println!("{}", y);
}

Saída:

1.414
área = 12.25
5 graus de distância do zero
1024
▶ Experimente você mesmo

Um sufixo no valor funciona do mesmo jeito: let x = 2.0_f64;. Use o que achar mais fácil de ler. Veja Anotações de tipo.

Correção 3: Dê um tipo à variável do laço ou ao argumento da closure

Seção intitulada “Correção 3: Dê um tipo à variável do laço ou ao argumento da closure”

Escolha esta quando o número vem de um intervalo (range), como for i in 1..=3 ou (1..=5).map(|n| ...). A variável i ou n tem o tipo {integer}, e não há nenhum let para anotar. O rustc aponta para a variável:

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

O rótulo embaixo de i diz “você precisa especificar um tipo para esta variável, como i32”.

Coloque o sufixo numa das pontas do intervalo, ou dê um tipo ao argumento da closure:

fn main() {
for i in 1..=3_u32 { // ✅ agora o intervalo inteiro é u32
println!("{} ao quadrado é {}", i, i.pow(2));
}
// ou um sufixo no intervalo...
let total: u32 = (1..=5_u32).map(|n| n.pow(2)).sum();
// ...ou um tipo no argumento da closure
let total2: u32 = (1..=5).map(|n: u32| n.pow(2)).sum();
println!("soma dos quadrados: {} e {}", total, total2);
}

Saída:

1 ao quadrado é 1
2 ao quadrado é 4
3 ao quadrado é 9
soma dos quadrados: 55 e 55
▶ Experimente você mesmo

Repare que let total: u32 = ... sozinho não basta: o rustc precisa do tipo de n dentro da closure, antes de chegar ao .sum(). Um sufixo no intervalo resolve tudo.

abs() num número negativo. let x = -5; x.abs() dá can't call method `abs` on ambiguous numeric type `{integer}`. Correção: let x: i32 = -5;. Cuidado com um sufixo no número: -5_i32.abs() significa -(5_i32.abs()), que dá -5. Use parênteses: (-5_i32).abs().

Uma conta rápida no main ou num teste. let area = 3.14 * r * r; funciona, porque * é um operador, e não um método. O erro só aparece quando você chama um método como .sqrt(), .round() ou .powi(). Correção: let r: f64 = 2.0;.

Um Vec de números. Com let nums = vec![1, 2, 3];, o nums.iter().map(|n| n.pow(2)) dá outro código, o E0599, no method named `pow` found for reference `&{integer}` (nenhum método pow encontrado para a referência &{integer}), porque aqui n é uma referência. A correção é a mesma: vec![1_i32, 2, 3] ou let nums: Vec<i32> = vec![1, 2, 3];.

Teste o que aprendeu

1. Qual linha compila?

2. Se nada mais decidir, que tipo o Rust dá a 7 e a 7.0?

3. for i in 0..10 { println!("{}", i.pow(2)); } dá E0689. Qual é a correção mais simples?

Pontuação: 0 / 3