Pular para o conteúdo

Erro E0284 do Rust: type annotations needed (falta o tipo no parse())

O E0284 quase sempre quer dizer uma coisa só: você chamou .parse() e o Rust não sabe em que tipo transformar o texto. A nota cannot satisfy `<_ as FromStr>::Err == _` parece assustadora, mas a correção costuma ter poucos caracteres, como : u32 ou ::<u32>. Esta página mostra a correção primeiro e depois decifra essa nota, para ela deixar de dar medo. Para os outros erros desta série, veja Erros do compilador Rust.

error[E0284]: type annotations needed
--> src/main.rs:2:9
|
2 | let age = "42".parse().unwrap();
| ^^^ ----- type must be known at this point
|
= note: cannot satisfy `<_ as FromStr>::Err == _`
help: consider giving `age` an explicit type
|
2 | let age: /* Type */ = "42".parse().unwrap();
| ++++++++++++

Em português: “anotações de tipo necessárias”. O rustc aponta para age, diz que o tipo precisa ser conhecido no parse e sugere dar um tipo explícito para age.

Este é o programa que gera o erro:

fn main() {
let age = "42".parse().unwrap();
println!("Ano que vem você vai ter {}", age + 1);
}

Você vê a mesma nota com .expect(...) em vez de .unwrap(), com match "42".parse() { ... } e com .parse() dentro de uma closure. Quando o .parse() não é guardado numa variável, o rustc aponta para o próprio parse e sugere parse::</* Type */>().

As marcas são a parte fácil:

  1. ^^^ embaixo de age: a variável cujo tipo é desconhecido.
  2. ----- embaixo de parse: “type must be known at this point” (o tipo precisa ser conhecido neste ponto). Foi aqui que o Rust travou.
  3. A ajuda: “consider giving age an explicit type” (considere dar um tipo explícito para age). Troque /* Type */ por um tipo de verdade, como u32.

Agora a nota, pedaço por pedaço. Nas mensagens do compilador, _ quer dizer “um tipo que ainda não conheço”:

Pedaço Em palavras simples
<_ as FromStr> “o tipo em que você está convertendo (ainda não sei qual é)”
::Err “…e o tipo de erro que vem junto com ele”
== _ “precisa ser igual a outro tipo que também não conheço”

Então a nota inteira diz: “Não consigo nem descobrir que tipo de erro o parse pode devolver, porque não sei em que você está convertendo.”

Pense em entregar uma carta a um tradutor e dizer “traduza isto”. Para francês? Japonês? Até você dizer, ele não pode começar, e nem consegue dizer que erros pode encontrar no caminho.

Esta é a assinatura do parse, um pouco simplificada:

fn parse<F: FromStr>(&self) -> Result<F, F::Err>

F é o tipo que você quer receber, e F::Err é o tipo de erro dele. Cada tipo de destino traz o seu: converter para i32 pode falhar com um ParseIntError, para f64 com um ParseFloatError, para bool com um ParseBoolError. O Rust precisa do F para saber o que o parse devolve.

A escolha também muda o que acontece em tempo de execução. "42" vira sem problema um u8, um i32 ou um f64. Mas "300" falha como u8 (grande demais), e "4.5" falha como i32 (não é um número inteiro). O Rust não chuta, porque um chute errado viraria um bug que só aparece com certas entradas. A lição Conversões mostra como a trait FromStr funciona e como implementá-la nos seus próprios tipos.

Como o Rust descobre os tipos sozinho está explicado na página do E0282. Por que existem três códigos diferentes (E0282, E0283, E0284) para “type annotations needed” está explicado na página do E0283, na seção sobre os três códigos. A versão curta: o E0284 é a variante em que a regra travada fala de um tipo que pertence ao tipo desconhecido (aqui, F::Err). Para você, a correção é a mesma: diga qual é o tipo.

Escolha esta na maioria dos casos. É o estilo que o Rust Book usa, e ainda documenta o que o texto deve conter:

fn main() {
let age: u32 = "42".parse().unwrap();
let price: f64 = "4.99".parse().unwrap();
let port: u16 = "8080".parse().expect("a porta precisa ser um número");
println!("Ano que vem você vai ter {}", age + 1);
println!("Dois cafés: {:.2}", price * 2.0);
println!("Escutando na porta {port}");
}

Saída:

Ano que vem você vai ter 43
Dois cafés: 9.98
Escutando na porta 8080
▶ Experimente você mesmo

Escolha o tipo que combina com o que o texto significa: u16 para uma porta, u8 para uma porcentagem, f64 para um preço. Um tipo pequeno demais transforma uma entrada válida em erro, então, na dúvida, escolha um tipo mais folgado, como i64 ou u32.

Escolha esta quando não há variável para anotar: dentro de uma closure, no meio de uma cadeia de iteradores ou numa expressão como ... * 2:

fn main() {
let line = "3 7 5";
let total = line
.split(' ')
.map(|s| s.parse::<u32>().unwrap())
.sum::<u32>();
let doubled = "21".parse::<i32>().unwrap() * 2;
println!("Total: {total}");
println!("Em dobro: {doubled}");
}

Saída:

Total: 15
Em dobro: 42
▶ Experimente você mesmo

Coloque o turbofish no próprio parse. Anotar só o resultado final muitas vezes não basta dentro de uma cadeia; veja o primeiro item de “Onde você costuma ver esse erro”, mais abaixo.

Escolha esta para entradas de verdade (arquivos, argumentos de linha de comando, formulários web), onde .unwrap() derrubaria o programa com um texto inválido. Diga o tipo no parse e faça match no Result. Repare como o tipo decide quais entradas dão certo:

fn main() {
for input in ["42", "300", "abc"] {
match input.parse::<u8>() {
Ok(n) => println!("{input}: virou {n}"),
Err(e) => println!("{input}: {e}"),
}
}
let rate: Result<f64, _> = "0.25".parse();
println!("{:?}", rate);
}

Saída:

42: virou 42
300: number too large to fit in target type
abc: invalid digit found in string
Ok(0.25)
▶ Experimente você mesmo

As mensagens de erro vêm da biblioteca padrão e são sempre em inglês: “number too large to fit in target type” (número grande demais para o tipo de destino) e “invalid digit found in string” (dígito inválido encontrado na string).

Em Result<f64, _>, você só diz o tipo do valor. O _ deixa o Rust preencher o tipo de erro (ParseFloatError), que é exatamente a parte sobre a qual a nota perguntava. Mais sobre isso em Result.

Correção 4: Use o valor num lugar onde o tipo é fixo

Seção intitulada “Correção 4: Use o valor num lugar onde o tipo é fixo”

Escolha esta quando o valor convertido vai para um campo de struct ou um parâmetro de função com tipo conhecido. O Rust lê a função inteira, então esse uso mais adiante responde à pergunta:

struct Config {
port: u16,
workers: u8,
}
fn greet(age: u32) {
println!("Bem-vindo! Você tem {age} anos.");
}
fn main() {
let port = "8080".parse().unwrap(); // u16, por causa do Config
let workers = "4".parse().unwrap(); // u8, por causa do Config
let config = Config { port, workers };
println!("Porta {} com {} workers", config.port, config.workers);
let age = "42".parse().unwrap(); // u32, por causa do greet
greet(age);
}

Saída:

Porta 8080 com 4 workers
Bem-vindo! Você tem 42 anos.
▶ Experimente você mesmo

Nem todo uso conta, e por isso esta é a Correção 4. Contas como age + 1 e comparações como age >= limit continuam dando E0284, mesmo que limit seja um u32: operadores funcionam entre muitos pares de tipos, então não apontam um tipo exato. E chamar um método, como age.pow(2), transforma o erro num E0282. Na dúvida, use a Correção 1.

Converter dentro de um map e depois sum(). let total: u32 = line.split(' ').map(|s| s.parse().unwrap()).sum(); continua sendo E0284, mesmo com total tendo um tipo. Um u32 pode ser a soma de valores u32 ou de valores &u32, então a anotação não diz ao Rust o que é cada item. Correção: s.parse::<u32>(). (Já coletar num Vec<i32> funciona, porque um Vec<i32> só pode guardar i32.)

Coletar os resultados do parse num Result. let nums: Result<Vec<_>, _> = text.split(' ').map(|x| x.parse()).collect(); é E0284: o _ em Vec<_> deixa o tipo dos itens em aberto. Correção: Result<Vec<i32>, _>. Esse padrão prático para no primeiro item inválido e te entrega o erro dele.

Ler a entrada do usuário. let guess = line.trim().parse().unwrap(); num jogo de adivinhação ou numa ferramenta de linha de comando. Correção: let guess: u32 = ..., exatamente como no Rust Book.

Com ? o erro é mais estranho. let port = text.parse()?; não dá E0284. Na edition 2021, o Rust recorre ao tipo () e mostra o E0277: the trait bound `(): FromStr` is not satisfied (o trait bound (): FromStr não é satisfeito; na edition 2024 aparece ! em vez de ()). A correção é a mesma: let port: u16 = text.parse()?;.

  • E0282: type annotations needed, quando não há pista nenhuma (ou quando o tipo é necessário para chamar um método, como age.pow(2)).
  • E0283: type annotations needed depois de .sum(), .collect() ou .into(), quando muitos tipos servem. Também explica por que são três códigos diferentes.
  • E0790: FromStr::from_str("42") ou Default::default() chamados sem dizer qual é o tipo.
  • E0277: o que o .parse()? te dá sem um tipo, e o que aparece quando o tipo escolhido não pode ser criado a partir de texto.
  • E0599: método não encontrado, por exemplo chamar .parse() em algo que não é texto, como uma variável u32 (nesse caso, converta com u8::try_from(n)).
  • Todos os erros do compilador: a lista completa de páginas de erro.

Teste o que aprendeu

1. O que cannot satisfy <_ as FromStr>::Err == _ quer dizer em palavras simples?

2. Qual linha corrige let port = "8080".parse().unwrap();?

3. let total: u32 = line.split(' ').map(|s| s.parse().unwrap()).sum(); ainda dá E0284. Onde o tipo deve ficar?

Pontuação: 0 / 3