Erro E0107 do Rust: takes N generic arguments but M were supplied (genéricos)
O E0107 quer dizer que o número de tipos dentro de <...> não bate com o que o tipo ou a função espera. O Result quer dois (Result<T, E>), o HashMap quer dois (HashMap<K, V>), o Option e o Vec querem um. Passe um a menos ou um a mais e o rustc diz algo como enum takes 2 generic arguments but 1 generic argument was supplied (o enum recebe 2 argumentos genéricos, mas foi passado 1), com “struct”, “enum”, “type alias”, “function” ou “method” no começo. É um dos erros mais amigáveis: o rustc te diz os dois números, e a correção é acrescentar ou tirar um tipo. Esta página faz parte do guia de erros do compilador.
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0107]: enum takes 2 generic arguments but 1 generic argument was supplied --> src/main.rs:1:29 |1 | fn parse_age(text: &str) -> Result<u32> { // ❌ `Result` needs two types | ^^^^^^ --- supplied 1 generic argument | | | expected 2 generic arguments |help: add missing generic argument |1 | fn parse_age(text: &str) -> Result<u32, E> { // ❌ `Result` needs two types | +++Em português: “o enum recebe 2 argumentos genéricos, mas foi passado 1”. A ajuda sugere acrescentar o argumento genérico que falta.
Este é o programa que gera o erro:
fn parse_age(text: &str) -> Result<u32> { // ❌ `Result` needs two types text.trim().parse()}
fn main() { for input in ["42", "forty"] { match parse_age(input) { Ok(age) => println!("age: {}", age), Err(e) => println!("error: {}", e), } }}(O comentário quer dizer “o Result precisa de dois tipos”. Ele fica em inglês porque aparece na saída do compilador.)
O que significa
Seção intitulada “O que significa”O rustc aponta para a linha 1 duas vezes, e depois dá uma ajuda:
- O
^^^^^^embaixo deResult: “expected 2 generic arguments” (eram esperados 2 argumentos genéricos). OResulté um enum com dois parâmetros de tipo:Tpara o valor de sucesso eEpara o erro. - O
---embaixo deu32: “supplied 1 generic argument” (foi passado 1 argumento genérico). Você só passou oT. - O
help:acrescenta umEprovisório. É uma dica de onde o tipo vai, não o tipo em si: você troca oEpelo tipo de erro de verdade.
Os argumentos genéricos são como os campos em branco de um formulário. O formulário do Result tem dois campos, “se der certo:” e “se der errado:”. Preencha só um, e o atendente te devolve o papel.
Esse erro é muito comum se você já leu código que escreve Result<u32> com um tipo só. Aquele código não estava errado: ele usava um type alias (apelido de tipo) como io::Result<T> ou anyhow::Result<T>, que preenche o tipo do erro para você. O Result normal, do prelude, não tem esse atalho.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”Um tipo genérico não é um tipo completo enquanto cada parâmetro não tiver um valor. Result<u32, E> com um E desconhecido não diz qual é o tamanho do valor nem como é um erro, então o Rust não consegue compilar código que usa isso. O Rust poderia tentar adivinhar o tipo que falta a partir do corpo da função, mas ele, de propósito, nunca deduz tipos na assinatura de uma função. A assinatura é o contrato em que o resto do código confia, então ela precisa ser escrita por inteiro.
Alguns parâmetros têm, sim, um valor padrão. HashMap<K, V, S = RandomState> tem um terceiro parâmetro para o hasher, e é por isso que o rustc diz “at least 2” (pelo menos 2) para um HashMap. Os valores padrão só podem preencher do fim para o começo, e só onde o autor do tipo colocou um. Veja Genéricos e Result.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Escreva o tipo que falta
Seção intitulada “Correção 1: Escreva o tipo que falta”Escolha esta na maioria das vezes. Descubra qual deve ser o parâmetro que falta e escreva-o. Para o parse(), o tipo de erro de um número que não pôde ser convertido é std::num::ParseIntError:
use std::num::ParseIntError;
fn parse_age(text: &str) -> Result<u32, ParseIntError> { text.trim().parse()}
fn main() { for input in ["42", "quarenta"] { match parse_age(input) { Ok(age) => println!("idade: {}", age), Err(e) => println!("erro: {}", e), } }}Saída:
idade: 42erro: invalid digit found in stringA mensagem invalid digit found in string (dígito inválido encontrado na string) vem da própria biblioteca padrão, por isso está em inglês. Não sabe qual é o tipo de erro? Olhe a documentação da função cujo erro você está devolvendo. A mesma correção funciona para HashMap<String>: diga o que são os valores, como em HashMap<String, u32>. Veja HashMap e Erros personalizados.
Correção 2: Use o alias de um tipo só que você queria, como io::Result
Seção intitulada “Correção 2: Use o alias de um tipo só que você queria, como io::Result”Escolha esta quando você trabalha com arquivos, rede ou stdin, ou quando viu Result<T> no código de outra pessoa. std::io::Result<T> é um atalho para Result<T, std::io::Error>. Importe o módulo e escreva io::Result:
use std::fs;use std::io;
fn read_config(path: &str) -> io::Result<String> { fs::read_to_string(path)}
fn main() { match read_config("missing-config.toml") { Ok(text) => println!("{}", text), Err(e) => println!("não foi possível ler a configuração: {}", e), }}Saída:
não foi possível ler a configuração: No such file or directory (os error 2)“No such file or directory” (arquivo ou diretório não encontrado) é a mensagem do sistema operacional. Apps que usam o crate anyhow escrevem anyhow::Result<T> do mesmo jeito. O erro contrário também é E0107: io::Result<String, io::Error> dá type alias takes 1 generic argument but 2 generic arguments were supplied (o type alias recebe 1 argumento genérico, mas foram passados 2), porque o alias já contém o tipo do erro. Tire o segundo. Veja Arquivos em Rust e anyhow e thiserror.
Correção 3: Deixe a sua função genérica, ou diga quais são os tipos
Seção intitulada “Correção 3: Deixe a sua função genérica, ou diga quais são os tipos”Escolha esta quando o tipo é uma struct genérica sua e você escreveu só o nome dela. O rustc diz missing generics for struct `Pair` (faltam os genéricos da struct Pair):
error[E0107]: missing generics for struct `Pair` --> src/main.rs:6:13 |6 | fn show(p: &Pair) { // ❌ which `K` and `V`? | ^^^^ expected 2 generic arguments |note: struct defined here, with 2 generic parameters: `K`, `V` --> src/main.rs:1:8 |1 | struct Pair<K, V> { | ^^^^ - -help: add missing generic arguments |6 | fn show(p: &Pair<K, V>) { // ❌ which `K` and `V`? | ++++++A nota mostra onde a struct foi definida, com os seus 2 parâmetros genéricos, K e V, e a ajuda sugere acrescentá-los. (O comentário em inglês pergunta “quais K e V?”.)
O programa com erro:
struct Pair<K, V> { key: K, value: V,}
fn show(p: &Pair) { // ❌ which `K` and `V`? println!("{} = {}", p.key, p.value);}
fn main() { show(&Pair { key: "volume", value: 7 }); show(&Pair { key: "theme", value: "dark" });}O help: escreve Pair<K, V>, mas K e V ainda não existem em show, então você também precisa declará-los. Como show é chamada com dois tipos diferentes de Pair, deixe-a genérica, com os bounds (restrições) de que ela precisa para imprimir:
use std::fmt::Display;
struct Pair<K, V> { key: K, value: V,}
fn show<K: Display, V: Display>(p: &Pair<K, V>) { println!("{} = {}", p.key, p.value);}
fn main() { show(&Pair { key: "volume", value: 7 }); show(&Pair { key: "tema", value: "escuro" });}Saída:
volume = 7tema = escuroSe a função só lida com um tipo, diga quais são os tipos: fn show(p: &Pair<&str, u32>). O mesmo vale para impl Pair { ... }, que precisa ser impl<K, V> Pair<K, V> { ... }. Veja Genéricos.
Correção 4: Tire o argumento a mais
Seção intitulada “Correção 4: Tire o argumento a mais”Escolha esta quando o rustc diz “but 2 generic arguments were supplied” (mas foram passados 2 argumentos genéricos) e quer menos. Isso acontece muito com o turbofish ::<> numa chamada de função:
error[E0107]: function takes 1 generic argument but 2 generic arguments were supplied --> src/main.rs:13:20 |13 | println!("{}", largest::<i32, i32>(&scores)); // ❌ one type too many | ^^^^^^^ ----- help: remove the unnecessary generic argument | | | expected 1 generic argument |note: function defined here, with 1 generic parameter: `T` --> src/main.rs:1:4 | 1 | fn largest<T: PartialOrd + Copy>(items: &[T]) -> T { | ^^^^^^^ -A ajuda sugere tirar o argumento genérico desnecessário, e a nota mostra a função com o seu único parâmetro, T.
O programa com erro:
fn largest<T: PartialOrd + Copy>(items: &[T]) -> T { let mut best = items[0]; for &item in items { if item > best { best = item; } } best}
fn main() { let scores = [72, 95, 88]; println!("{}", largest::<i32, i32>(&scores)); // ❌ one type too many}largest tem um parâmetro, T, então ela recebe um tipo. Escreva largest::<i32>(...), ou deixe o turbofish de fora de vez, já que o Rust descobre o T pelo argumento:
fn largest<T: PartialOrd + Copy>(items: &[T]) -> T { let mut best = items[0]; for &item in items { if item > best { best = item; } } best}
fn main() { let scores = [72, 95, 88]; println!("{}", largest(&scores)); println!("{}", largest::<f64>(&[1.5, 0.5]));}Saída:
951.5A note: é a linha mais útil aqui: ela mostra a definição e lista os parâmetros, para você mesmo contar.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”HashMap<String> só com a chave. O rustc diz struct takes at least 2 generic arguments but 1 generic argument was supplied. Correção: HashMap<String, u32>, ou escreva HashMap::new() e deixe o Rust deduzir os dois a partir do primeiro insert. Veja HashMap.
A sua própria struct com dois parâmetros de tipo, recebendo um. let p: Pair<&str> = ... dá struct takes 2 generic arguments but 1 generic argument was supplied. Correção: Pair<&str, i32>, ou Pair<_, _> para o Rust preencher os dois. Option<i32, i32> é o contrário: enum takes 1 generic argument but 2 generic arguments were supplied. Se você queria dois valores, use uma tupla, Option<(i32, i32)>.
Lifetimes também contam. Uma struct Excerpt<'a> escrita como Excerpt<'a, 'a> dá struct takes 1 lifetime argument but 2 lifetime arguments were supplied. Correção: uma lifetime para cada parâmetro da definição. Veja Lifetimes. Um método que nem é genérico, como .into::<String>(), dá method takes 0 generic arguments but 1 generic argument was supplied; escreva let s: String = x.into(); (veja E0283).
Uma surpresa: Vec<i32, i32> não dá E0107. O Vec tem um segundo parâmetro, instável, para o alocador de memória, então você recebe o E0658 (em inglês; allocator_api) e alguns outros erros. A correção é a mesma: Vec<i32>, ou Vec<(i32, i32)> para pares.
Erros relacionados
Seção intitulada “Erros relacionados”- E0061: o mesmo erro com argumentos normais, uma função que recebe N argumentos, mas recebeu M.
- E0106: falta o especificador de lifetime, quando uma referência num tipo precisa de um
'a. - E0252: duas linhas
useimportam o mesmo nome; muitas vezes vem um E0107 depois, quandoResultpassa a ser ofmt::Result. - E0283: anotações de tipo necessárias, a correção certa quando
.into::<T>()dá E0107. - E0401: uma função interna usa o
Tgenérico da função de fora. - 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. fn load() -> Result<String> dá E0107. Qual tipo de retorno corrige isso numa função que lê um arquivo?
2. Por que o rustc diz at least 2 generic arguments para o HashMap?
3. fn show(p: &Pair) dá `missing generics for struct Pair , e show é chamada com tipos diferentes de Pair`. Qual é a correção?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0107 — Rust error codes index (em inglês): a explicação oficial, com exemplos de argumentos genéricos e de lifetime demais e de menos.
- Generic Data Types — The Rust Book (em inglês): como o
<T>funciona em structs comoPoint<T, U>, enums comoResult<T, E>e métodos. - Recoverable Errors with
Result— The Rust Book (em inglês): funções que devolvemResult<String, io::Error>, e o operador?. std::io::Result— documentação da biblioteca padrão (em inglês): o alias de um parâmetro só, e por que tanto código escreveio::Result<T>.- Generic parameters — The Rust Reference (em inglês): as regras exatas, incluindo parâmetros de tipo com valor padrão, como o hasher do
HashMap.