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.)
A mensagem de erro
Seção intitulada “A mensagem de erro”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);}O que significa
Seção intitulada “O que significa”{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.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”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.
Como corrigir
Seção intitulada “Como corrigir”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 = 10242^40 = 1099511627776sqrt(2) = 1.4142135623730951|-7| = 7Escolha 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.
Correção 2: Dê um tipo à variável
Seção intitulada “Correção 2: Dê um tipo à variável”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.255 graus de distância do zero1024Um 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 é 12 ao quadrado é 43 ao quadrado é 9soma dos quadrados: 55 e 55Repare 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.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”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];.
Erros relacionados
Seção intitulada “Erros relacionados”- E0282: type annotations needed: o Rust não consegue inferir um tipo de jeito nenhum, por exemplo em
Vec::new()ou.collect(). A correção segue a mesma ideia: diga o tipo. - E0610: primitive type doesn’t have fields:
.alguma_coisasem()num{integer}, comox.absem vez dex.abs(). - E0599: no method named x found: o tipo é conhecido, mas não tem esse método, ou (como acima) é uma referência a um
{integer}. - E0308: mismatched types: o que você pode ver em seguida, se o tipo escolhido não combinar com o lugar onde o número é usado.
- Todos os erros do compilador explicados
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”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
Para saber mais
Seção intitulada “Para saber mais”- E0689 — Rust error codes index (em inglês): a explicação oficial deste erro.
- Data Types — The Rust Book (em inglês): tipos inteiros e de ponto flutuante, números com sufixos como
57u8e os padrõesi32/f64. - Literals — Rust by Example (em inglês): como sufixos como
1u32e1.0f64escolhem um tipo. - Integer literal expressions — The Rust Reference (em inglês): as regras exatas de como um número escrito no código ganha o seu tipo, incluindo o padrão
i32. f64::sqrt— documentação da biblioteca padrão (em inglês): os métodos de ponto flutuante (sqrt,powi,round…) que precisam de um tipo conhecido.