Ir al contenido

Error E0107 de Rust: wrong number of generic arguments (faltan o sobran tipos)

E0107 significa que el número de tipos entre <...> no coincide con lo que espera el tipo o la función. Result quiere dos (Result<T, E>), HashMap quiere dos (HashMap<K, V>), Option y Vec quieren uno. Si pones uno de menos o uno de más, rustc dice algo como enum takes 2 generic arguments but 1 generic argument was supplied (el enum recibe 2 argumentos genéricos, pero se dio 1), con “struct”, “enum”, “type alias”, “function” o “method” al principio. Es uno de los errores más amables: rustc te dice los dos números, y la solución es añadir o quitar un tipo. Forma parte de la guía de errores del 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
| +++

Este es el programa que lo produce:

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),
}
}
}

rustc señala la línea 1 dos veces:

  1. El ^^^^^^ debajo de Result: “expected 2 generic arguments” (se esperaban 2 argumentos genéricos). Result es un enum con dos parámetros de tipo: T para el valor si todo va bien y E para el error.
  2. El --- debajo de u32: “supplied 1 generic argument” (se dio 1 argumento genérico). Solo diste T.
  3. El help: añade una E de relleno. Es una pista de dónde va el tipo, no el tipo en sí: tienes que cambiar E por el tipo de error real.

Los argumentos genéricos son como los huecos de un formulario. El formulario de Result tiene dos huecos, “si funciona:” y “si falla:”. Si rellenas solo uno, en la ventanilla te lo devuelven.

Este error es muy común si has leído código que escribe Result<u32> con un solo tipo. Ese código no estaba mal: usaba un alias de tipo (type alias) como io::Result<T> o anyhow::Result<T>, que rellena el tipo de error por ti. El Result normal del prelude no tiene ese atajo.

Un tipo genérico no es un tipo completo hasta que cada parámetro tiene un valor. Result<u32, E> con una E desconocida no dice cuánto ocupa el valor ni cómo es un error, así que Rust no puede compilar el código que lo usa. Rust podría intentar adivinar el tipo que falta a partir del cuerpo de la función, pero a propósito nunca deduce tipos en la firma de una función. Las firmas son el contrato del que depende el resto del código, así que tienen que estar escritas completas.

Algunos parámetros sí tienen un valor por defecto. HashMap<K, V, S = RandomState> tiene un tercer parámetro para el hasher, y por eso rustc dice “at least 2” (al menos 2) para un HashMap. Los valores por defecto solo pueden rellenar los últimos parámetros, y solo donde el autor del tipo puso uno. Mira Genéricos y Result.

Elige esta casi siempre. Averigua qué debería ser el parámetro que falta y escríbelo. Para parse(), el tipo de error de un número que no se puede convertir es std::num::ParseIntError:

use std::num::ParseIntError;
fn parse_age(text: &str) -> Result<u32, ParseIntError> {
text.trim().parse()
}
fn main() {
for input in ["42", "cuarenta"] {
match parse_age(input) {
Ok(age) => println!("edad: {}", age),
Err(e) => println!("error: {}", e),
}
}
}

Salida:

edad: 42
error: invalid digit found in string
▶ Pruébalo tú mismo

El mensaje “invalid digit found in string” (se encontró un dígito no válido en el texto) lo escribe la propia biblioteca estándar, por eso sale en inglés. ¿No sabes qué tipo de error es? Mira la documentación de la función cuyo error devuelves. La misma solución sirve para HashMap<String>: di qué son los valores, como en HashMap<String, u32>. Mira HashMaps y Errores personalizados.

Solución 2: Usa el alias de un solo tipo que tenías en mente, como io::Result

Sección titulada «Solución 2: Usa el alias de un solo tipo que tenías en mente, como io::Result»

Elige esta cuando trabajas con archivos, la red o la entrada estándar, o cuando viste Result<T> en código de otra persona. std::io::Result<T> es la forma corta de Result<T, std::io::Error>. Importa el módulo y escribe 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!("no se pudo leer la configuración: {}", e),
}
}

Salida:

no se pudo leer la configuración: No such file or directory (os error 2)
▶ Pruébalo tú mismo

“No such file or directory” (no existe el archivo o el directorio) es el mensaje del sistema operativo. Las aplicaciones que usan el crate anyhow escriben anyhow::Result<T> de la misma manera. El error contrario también es E0107: io::Result<String, io::Error> da type alias takes 1 generic argument but 2 generic arguments were supplied, porque el alias ya contiene el tipo de error. Quita el segundo. Mira Leer y escribir archivos y anyhow y thiserror.

Solución 3: Haz genérica tu función, o di qué tipos son

Sección titulada «Solución 3: Haz genérica tu función, o di qué tipos son»

Elige esta cuando el tipo es un struct genérico tuyo y escribiste solo su nombre. rustc dice missing generics for struct `Pair` (faltan los genéricos del 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`?
| ++++++

El programa con el error:

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" });
}

El help: escribe Pair<K, V>, pero K y V todavía no existen en show, así que también tienes que declararlos. Como show se llama con dos clases distintas de Pair, hazla genérica, con las restricciones que necesita 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: "volumen", value: 7 });
show(&Pair { key: "tema", value: "oscuro" });
}

Salida:

volumen = 7
tema = oscuro
▶ Pruébalo tú mismo

Si la función solo maneja una clase, escribe los tipos: fn show(p: &Pair<&str, u32>). Lo mismo pasa con impl Pair { ... }, que tiene que ser impl<K, V> Pair<K, V> { ... }. Mira Genéricos.

Elige esta cuando rustc dice “but 2 generic arguments were supplied” (pero se dieron 2 argumentos genéricos) y quiere menos. Suele pasar con el turbofish ::<> al llamar a una función:

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 {
| ^^^^^^^ -

El programa con el error:

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 tiene un parámetro, T, así que recibe un tipo. Escribe largest::<i32>(...), o quita el turbofish del todo, porque Rust puede deducir T a partir del 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]));
}

Salida:

95
1.5
▶ Pruébalo tú mismo

La note: es la línea más útil aquí: muestra la definición y enumera sus parámetros, así que puedes contarlos tú mismo.

HashMap<String> solo con la clave. rustc dice struct takes at least 2 generic arguments but 1 generic argument was supplied. Solución: HashMap<String, u32>, o escribe HashMap::new() y deja que Rust deduzca los dos tipos a partir del primer insert. Mira HashMaps.

Tu propio struct con dos parámetros de tipo, con solo uno. let p: Pair<&str> = ... da struct takes 2 generic arguments but 1 generic argument was supplied. Solución: Pair<&str, i32>, o Pair<_, _> para que Rust rellene los dos. Option<i32, i32> es lo contrario: enum takes 1 generic argument but 2 generic arguments were supplied. Si querías dos valores, usa una tupla, Option<(i32, i32)>.

Los lifetimes también cuentan. Un struct Excerpt<'a> escrito como Excerpt<'a, 'a> da struct takes 1 lifetime argument but 2 lifetime arguments were supplied. Solución: un lifetime por cada parámetro de la definición. Mira Lifetimes. Un método que no es genérico en absoluto, como .into::<String>(), da method takes 0 generic arguments but 1 generic argument was supplied; escribe let s: String = x.into(); en su lugar (mira E0283).

Una sorpresa: Vec<i32, i32> no es E0107. Vec tiene un segundo parámetro, inestable, para el asignador de memoria, así que obtienes E0658 (en inglés; allocator_api) y algunos errores más. La solución es la misma: Vec<i32>, o Vec<(i32, i32)> para pares.

  • E0061: el mismo error con argumentos normales, una función que recibe N argumentos pero se le dieron M.
  • E0106: falta un especificador de lifetime, cuando una referencia dentro de un tipo necesita un 'a.
  • E0252: dos líneas use importan el mismo nombre; a menudo le sigue un E0107 cuando Result significa fmt::Result.
  • E0283: hacen falta anotaciones de tipo; es la solución correcta cuando .into::<T>() da E0107.
  • E0401: una función interna usa el T genérico de la función externa.
  • Todos los errores del compilador: la lista completa de páginas de errores.

Comprueba lo aprendido

1. fn load() -> Result<String> da E0107. ¿Qué tipo de retorno lo arregla en una función que lee un archivo?

2. ¿Por qué rustc dice at least 2 generic arguments (al menos 2) para HashMap?

3. fn show(p: &Pair) da `missing generics for struct Pair , y show se llama con distintas clases de Pair`. ¿Cómo se arregla?

Puntuación: 0 / 3