Erro E0282 do Rust: type annotations needed (falta anotação de tipo)
O E0282 quer dizer que o Rust tentou descobrir um tipo sozinho e não encontrou pistas suficientes. Não há nada errado com a sua lógica. O compilador só precisa que você diga o nome de um tipo, normalmente com alguns caracteres a mais. Esta página mostra a correção primeiro e depois explica como funciona a dedução de tipos do Rust (a inferência de tipos, ou type inference), para você saber quando dar uma ajuda. Para os outros erros desta série, veja Erros do compilador Rust.
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0282]: type annotations needed for `Vec<_>` --> src/main.rs:2:9 |2 | let mut scores = Vec::new(); // um Vec de... quê? | ^^^^^^^^^^ ---------- type must be known at this point |help: consider giving `scores` an explicit type, where the type for type parameter `T` is specified |2 | let mut scores: Vec<T> = Vec::new(); // um Vec de... quê? | ++++++++Em português: “são necessárias anotações de tipo para Vec<_>”. O compilador diz que precisa saber o tipo naquele ponto e sugere dar a scores um tipo explícito, informando o tipo do parâmetro T.
Este é o programa que gera o erro:
fn main() { let mut scores = Vec::new(); // um Vec de... quê? println!("Scores recorded: {}", scores.len());}Mesma mensagem, outro código
Seção intitulada “Mesma mensagem, outro código”As palavras “type annotations needed” também aparecem com outros três códigos. Eles querem dizer a mesma coisa, e as correções desta página servem para todos:
- E0283:
.collect()ou.sum()sem um tipo de destino. O rustc acrescenta uma nota comothe type must implement `FromIterator<&str>`ou`Sum<&i32>`(o tipo precisa implementar…), porque muitos tipos poderiam ser o resultado. - E0284:
"42".parse().unwrap()sem um tipo de destino. A nota dizcannot satisfy `<_ as FromStr>::Err == _`, que é um jeito assustador de dizer “converter para quê?”. - E0790:
let settings = Default::default();. A primeira linha dizcannot call associated function on trait without specifying the corresponding `impl` type(não dá para chamar a função associada da trait sem dizer o tipo doimpl): o Rust não sabe de quem é o valor padrão que você quer.
O código que você recebe depende da linha exata, e pode mudar se você acrescentar mais código. Por exemplo, let age = "42".parse().unwrap(); dá E0284, mas se a linha seguinte chamar age.pow(2), vira E0282. Não se preocupe com o número; procure a variável que o rustc aponta.
O que significa
Seção intitulada “O que significa”Olhe as duas marcas embaixo da linha 2:
^^^^^^^^^^embaixo demut scores: esta é a variável cujo tipo o Rust não conseguiu descobrir.----------embaixo deVec::new(): “type must be known at this point” (o tipo precisa ser conhecido neste ponto).Vec::new()cria uma lista vazia, mas uma lista vazia de quê? Números? Nomes? O Rust precisa saber para reservar o tipo certo de espaço.- A ajuda sugere
let mut scores: Vec<T>. AquiTé só um marcador. Você o troca por um tipo de verdade, comoVec<u32>.
Pense numa caixa nos Correios com o campo “conteúdo” em branco. O atendente não pode enviá-la até alguém escrever alguma coisa ali. Você pode escrever, ou colocar algo dentro da caixa para o atendente ver.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”Todo valor em Rust tem um tipo exato, decidido em tempo de compilação. Na maioria das vezes você não escreve esse tipo, porque o Rust lê o seu código e descobre sozinho. let count = 5; é um i32. let name = String::from("Ferris"); é uma String. Isso é inferência de tipos, e a lição Variáveis mostra mais exemplos.
A inferência usa pistas da função inteira, não de uma linha só. Se você chamar scores.push(90) mais adiante, isso é uma pista: o Vec guarda números. Se passar scores para uma função que recebe &[u32], a pista é ainda melhor. O E0282 aparece quando não há pista nenhuma, ou quando o Rust precisa do tipo antes de chegar à pista (por exemplo, para procurar um método).
Algumas funções são genéricas no tipo de retorno: parse, collect, sum, Default::default, Vec::new, None. Elas podem produzir muitos tipos diferentes, então o Rust não consegue escolher um por você. E ele não chuta, porque um chute errado mudaria o comportamento do programa sem você perceber. Veja Genéricos para entender como uma função pode retornar muitos tipos.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Anote a variável
Seção intitulada “Correção 1: Anote a variável”Escolha esta na maioria dos casos. Coloque : Tipo depois do nome da variável. É curto e ainda documenta o código para a próxima pessoa que for ler:
fn main() { let scores: Vec<u32> = Vec::new(); let age: u32 = "42".parse().unwrap(); let words: Vec<&str> = "vermelho verde azul".split(' ').collect(); let winner: Option<String> = None;
println!("Pontuações registradas: {}", scores.len()); println!("Ano que vem você vai ter {}", age + 1); println!("{:?}", words); println!("{:?}", winner);}Saída:
Pontuações registradas: 0Ano que vem você vai ter 43["vermelho", "verde", "azul"]NoneVocê nem sempre precisa escrever o tipo completo. let words: Vec<_> = ...collect(); quer dizer “um Vec, e você descobre o que vai dentro”. O _ pede ao Rust para completar essa parte. Mais sobre como escrever tipos em Tipos de dados.
Correção 2: Use o turbofish ::<>
Seção intitulada “Correção 2: Use o turbofish ::<>”Escolha esta quando não há variável para anotar, por exemplo no meio de uma cadeia de chamadas de iteradores. Você coloca o tipo direto na chamada da função, com ::<Tipo>. O pessoal chama o ::<> de “turbofish” (peixe-turbo) porque ele lembra um pouco um peixe:
fn main() { let input = "4 15 7"; let numbers = input .split(' ') .map(|s| s.parse::<u32>().unwrap()) .collect::<Vec<_>>(); let total = numbers.iter().sum::<u32>();
println!("{:?}", numbers); println!("Total: {total}");}Saída:
[4, 15, 7]Total: 26As três chamadas com turbofish mais comuns são parse::<i32>(), collect::<Vec<_>>() e sum::<i32>(). A Correção 1 e a Correção 2 são igualmente certas; let total: u32 = numbers.iter().sum(); faz o mesmo trabalho. Escolha a que ficar mais fácil de ler.
Correção 3: Use o valor, para o Rust inferir o tipo
Seção intitulada “Correção 3: Use o valor, para o Rust inferir o tipo”Escolha esta quando o erro aparece porque o código ainda não está pronto. Muitas vezes você cria um Vec vazio, e o código que o preenche vem depois. Assim que você usa o valor, o Rust tem a pista dele e o erro some, sem precisar de anotação:
fn print_team(names: &[String]) { for name in names { println!("- {name}"); }}
fn main() { let mut team = Vec::new(); // tipo desconhecido... por enquanto team.push(String::from("Ferris")); // agora o Rust sabe: Vec<String> team.push(String::from("Corro")); print_team(&team);}Saída:
- Ferris- CorroA ordem importa um pouco. A pista precisa vir antes de o Rust precisar do tipo para chamar um método. Uma closure como let shout = |s| s.to_uppercase(); continua falhando, mesmo que você chame shout("hi") na linha seguinte, porque o Rust precisa saber o que é s para encontrar o .to_uppercase(). Nesse caso, escreva o tipo: |s: &str| s.to_uppercase(). Veja Closures.
Correção 4: Diga o tipo de None e de Default::default()
Seção intitulada “Correção 4: Diga o tipo de None e de Default::default()”Escolha esta para valores que não trazem pista nenhuma. None não diz o que guardaria, e Default::default() não diz de quem é o valor padrão que você quer. Escreva o tipo dentro da expressão:
#[derive(Debug, Default)]struct Settings { volume: u8, muted: bool,}
fn main() { let fastest_lap = None::<f64>; // em vez de: None let settings = Settings::default(); // em vez de: Default::default() let backup: Settings = Default::default(); // uma anotação também funciona
println!("{:?}", fastest_lap); println!("{:?}", settings); println!("Volume do backup: {}", backup.volume);}Saída:
NoneSettings { volume: 0, muted: false }Volume do backup: 0Settings::default() é a escolha mais clara: dá para ler como “o Settings padrão”. None::<f64> é prático, mas let fastest_lap: Option<f64> = None; (Correção 1) é mais comum. Veja Option.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”Convertendo texto em número. let guess = line.trim().parse().unwrap(); dá E0284 (ou E0282, se você chamar um método em guess logo depois). parse pode produzir um i32, um u8, um f64 e muitos outros. Correção: let guess: i32 = ... ou .parse::<i32>(). Comparar guess com uma variável i32 mais adiante não basta aqui, porque o parse precisa do tipo na própria chamada.
.collect(). Ele pode montar um Vec, uma String, um HashSet, um HashMap… então sempre precisa saber o destino. Correção: let words: Vec<_> = ...collect(); ou .collect::<Vec<_>>(). Veja Iteradores.
.sum() e .product(). nums.iter().sum() dá E0283 com the type must implement `Sum<&i32>`. Somar i32 poderia dar um i32, mas outros tipos numéricos também podem ser somas. Correção: let total: i32 = nums.iter().sum(); ou .sum::<i32>().
Uma coleção vazia no construtor de uma struct. let cache = HashMap::new(); numa função new() que você ainda não terminou de escrever. Correção: escreva o tipo completo uma vez (HashMap<String, u32>), ou retorne o valor numa struct cujo campo já tem o tipo declarado, que é uma pista que o Rust consegue usar.
Erros relacionados
Seção intitulada “Erros relacionados”- E0308: tipos incompatíveis. Acontece quando a sua anotação e o valor discordam, como em
let age: u32 = "42";. - E0277: restrição de trait não satisfeita. Você informou um tipo, mas ele não pode ser montado daquele jeito, como em
let n: Vec<i32> = "1 2".split(' ').collect();(isso é umVec<&str>; converta cada item antes). - E0599: método não encontrado, por exemplo quando você anota
let n: u32e depois chama nele um método deString, como.trim(). - E0004: padrões não exaustivos, outro erro em que o rustc escreve a correção para você.
- Todos os erros do compilador: a lista completa de páginas de erro.
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. Qual linha corrige o E0282 em let ids = Vec::new(); quando o Vec deve guardar valores u64?
2. Por que let n = "7".parse().unwrap(); precisa de um tipo, se o texto é claramente um número?
3. O que o _ quer dizer em .collect::<Vec<_>>()?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0282 — Rust error codes index (em inglês): a explicação oficial deste erro, com mais exemplos.
- E0283 — Rust error codes index (em inglês): a variante que você vai ver com
collectesum, quando muitos tipos serviriam. - Data Types — The Rust Book (em inglês): explica as anotações, incluindo o exemplo clássico do
"42".parse(). - Inference — Rust by Example (em inglês): um exemplo curto do Rust inferindo o tipo de um
Veca partir do código seguinte. Iterator::collect— documentação da biblioteca padrão (em inglês): mostra tanto o estilo com anotação quanto o estilo com turbofish.