Pular para o conteúdo

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.

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 rustc aponta para a linha 1 duas vezes, e depois dá uma ajuda:

  1. O ^^^^^^ embaixo de Result: “expected 2 generic arguments” (eram esperados 2 argumentos genéricos). O Result é um enum com dois parâmetros de tipo: T para o valor de sucesso e E para o erro.
  2. O --- embaixo de u32: “supplied 1 generic argument” (foi passado 1 argumento genérico). Você só passou o T.
  3. O help: acrescenta um E provisório. É uma dica de onde o tipo vai, não o tipo em si: você troca o E pelo 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.

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.

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: 42
erro: invalid digit found in string
▶ Experimente você mesmo

A 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)
▶ Experimente você mesmo

“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 = 7
tema = escuro
▶ Experimente você mesmo

Se 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.

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:

95
1.5
▶ Experimente você mesmo

A note: é a linha mais útil aqui: ela mostra a definição e lista os parâmetros, para você mesmo contar.

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.

  • 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 use importam o mesmo nome; muitas vezes vem um E0107 depois, quando Result passa a ser o fmt::Result.
  • E0283: anotações de tipo necessárias, a correção certa quando .into::<T>() dá E0107.
  • E0401: uma função interna usa o T genérico da função de fora.
  • Todos os erros do compilador: a lista completa de páginas de erro.

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