Ir al contenido

Error E0057 de Rust: this function takes N arguments but M were supplied (closures)

E0057 significa que llamaste a una closure (función anónima) con un número equivocado de argumentos: demasiado pocos o demasiados. Es la versión para closures de E0061, y el mensaje dice casi lo mismo. La solución suele ser rápida: mira el |...| de la closure y pasa un valor por cada parámetro. Esta página también cubre dos parientes cercanos que te encontrarás con los iteradores, E0593 y E0631, porque vienen de la misma confusión. Para los demás errores de esta serie, mira Errores del compilador de Rust.

error[E0057]: this function takes 2 arguments but 1 argument was supplied
--> src/main.rs:3:17
|
3 | let total = add(1); // ❌ add quiere dos números
| ^^^--- argument #2 of type `i32` is missing
|
note: closure defined here
--> src/main.rs:2:15
|
2 | let add = |a: i32, b: i32| a + b;
| ^^^^^^^^^^^^^^^^
help: provide the argument
|
3 | let total = add(1, /* i32 */); // ❌ add quiere dos números
| +++++++++++

En español: “esta función recibe 2 argumentos, pero se pasó 1”. La etiqueta dice que falta el argumento n.º 2, de tipo i32, y la ayuda (help: provide the argument) muestra dónde ponerlo.

Este es el programa que lo provoca:

fn main() {
let add = |a: i32, b: i32| a + b;
let total = add(1); // ❌ add quiere dos números
println!("{total}");
}

Pasar demasiados argumentos da el mismo código, con una etiqueta “unexpected argument” (argumento inesperado) y una ayuda que dice “remove the extra argument” (quita el argumento de más):

error[E0057]: this function takes 1 argument but 2 arguments were supplied
--> src/main.rs:3:5
|
3 | greet("Ferris", "Corro");
| ^^^^^ ------- unexpected argument #2 of type `&'static str`
|
note: closure defined here

Lee el mensaje igual que el de las funciones: la primera línea cuenta (la closure recibe 2, tú le diste 1), la etiqueta debajo de tu llamada nombra el argumento que falta o que sobra, y help: muestra dónde va. /* i32 */ es solo un comentario de relleno que te dice el tipo. Pon ahí un valor de verdad.

¿E0057 o E0061? La primera línea es la misma. La note: los distingue:

  • closure defined here (closure definida aquí): llamaste a una closure. Eso es E0057.
  • callable defined here (invocable definido aquí): llamaste a un f genérico con una restricción como F: Fn(i32). También es E0057.
  • function defined here (función definida aquí): una fn normal, un método o un puntero a función (let f: fn(i32, i32) -> i32 = add;). Eso es E0061.

En cualquier caso, la solución es la misma: haz que la llamada coincida con la lista de parámetros.

La lista de parámetros de una closure es tan fija como la de una función. |a: i32, b: i32| recibe exactamente dos i32, siempre. No hay argumentos por defecto ni “pasa menos si quieres”. Incluso cuando no escribes los tipos, rustc los deduce a partir del cuerpo de la closure y de su primer uso, y desde ese momento quedan fijos.

Por dentro, una closure implementa uno de los traits Fn con una lista fija de tipos de argumento, como Fn(i32, i32) -> i32. El compilador comprueba cada llamada contra esa lista, así que una llamada con el número equivocado nunca llega a tu programa en ejecución. Cómo funcionan las closures y sus parámetros se explica en Closures y en Traits de closures.

Solución 1: Haz que la llamada coincida con los parámetros de la closure

Sección titulada «Solución 1: Haz que la llamada coincida con los parámetros de la closure»

Elige esta casi siempre. Ve a la ubicación que indica la note:, cuenta los parámetros entre las barras |...| y pasa un valor por cada uno. Si tienes más valores que parámetros, llama a la closure más de una vez:

fn main() {
let add = |a: i32, b: i32| a + b;
let greet = |name: &str| println!("¡Hola, {name}!");
let total = add(1, 2);
println!("{total}");
greet("Ferris");
greet("Corro"); // una llamada por cada nombre
}

Salida:

3
¡Hola, Ferris!
¡Hola, Corro!
▶ Pruébalo tú mismo

Si la closure de verdad debería recibir otra cantidad de valores, cambia la closure. Eso también está bien, siempre que todas las llamadas estén de acuerdo con ella.

Solución 2: Tuplas: una tupla es un solo argumento

Sección titulada «Solución 2: Tuplas: una tupla es un solo argumento»

Elige esta cuando hay tuplas de por medio. |(name, score)| y |name, score| se parecen, pero son closures distintas. La primera recibe un argumento, una tupla, y la desempaqueta. La segunda recibe dos argumentos.

Llamar a una closure que espera una tupla con dos valores da E0057, y rustc sugiere añadir los paréntesis:

error[E0057]: function takes 1 argument but 2 arguments were supplied
--> src/main.rs:3:5
|
3 | show("Ferris", 90);
| ^^^^
|
note: closure parameter defined here
help: wrap these arguments in parentheses to construct a tuple
|
3 | show(("Ferris", 90));
| + +

La ayuda dice: “envuelve estos argumentos entre paréntesis para construir una tupla”.

El error contrario pasa todo el tiempo con los iteradores. zip, enumerate y recorrer un mapa le pasan a tu closure una tupla por elemento. Escribir .map(|name, score| ...) da otro código, E0593:

error[E0593]: closure is expected to take a single 2-tuple as argument, but it takes 2 distinct arguments
--> src/main.rs:7:10
|
7 | .map(|name, score| format!("{name}: {score}"))
| ^^^ ------------- takes 2 distinct arguments
| |
| expected closure that takes a single 2-tuple as argument
|
help: change the closure to accept a tuple instead of individual arguments
|
7 - .map(|name, score| format!("{name}: {score}"))
7 + .map(|(name, score)| format!("{name}: {score}"))

En español: “se esperaba que la closure recibiera una sola tupla de 2 elementos, pero recibe 2 argumentos distintos”. La ayuda propone cambiar la closure para que acepte una tupla.

Los dos tienen la misma solución: pon los paréntesis donde está la tupla.

fn main() {
let names = vec!["Ferris", "Corro"];
let scores = vec![90, 75];
// zip da una tupla (name, score) por elemento: desempaquétala en la lista de parámetros
let lines: Vec<String> = names
.iter()
.zip(scores.iter())
.map(|(name, score)| format!("{name}: {score}"))
.collect();
println!("{:?}", lines);
// una closure que recibe una tupla se llama con una tupla
let show = |(name, score): (&str, i32)| println!("{name} sacó {score} puntos");
show(("Ferris", 90));
}

Salida:

["Ferris: 90", "Corro: 75"]
Ferris sacó 90 puntos
▶ Pruébalo tú mismo

Lo mismo vale para .enumerate().map(|(i, item)| ...) y para map.iter().for_each(|(key, value)| ...). Mira Iteradores y Tuplas.

Solución 3: Adapta con una closure envoltorio

Sección titulada «Solución 3: Adapta con una closure envoltorio»

Elige esta cuando pasas una función o closure a otra función (una “función de orden superior”) y el número de parámetros no encaja. Aquí apply quiere algo que reciba un i32, pero add recibe dos. Eso vuelve a dar E0593:

error[E0593]: function is expected to take 1 argument, but it takes 2 arguments
--> src/main.rs:10:26
|
5 | fn add(a: i32, b: i32) -> i32 {
| ----------------------------- takes 2 arguments
...
10 | println!("{}", apply(add, 5));
| ----- ^^^ expected function that takes 1 argument
| |
| required by a bound introduced by this call

En español: “se esperaba que la función recibiera 1 argumento, pero recibe 2”.

No cambies add. Envuélvela en una closure pequeña que tenga la forma que apply espera y que ponga ella misma el valor que falta:

fn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
f(x)
}
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
let bonus = 10;
// recibe 1 argumento, llama a add con 2
println!("{}", apply(|x| add(x, bonus), 5));
// recibe 1 tupla, llama a add con sus dos partes
let pairs = vec![(1, 2), (3, 4)];
let sums: Vec<i32> = pairs.into_iter().map(|(a, b)| add(a, b)).collect();
println!("{:?}", sums);
}

Salida:

15
[3, 7]
▶ Pruébalo tú mismo

El envoltorio toma prestado bonus del código que lo rodea, que es justo lo que mejor saben hacer las closures. Pasar una función por su nombre (apply(double, 5)) solo funciona cuando sus parámetros ya coinciden. Mira Traits de closures.

Dentro de una función genérica. Una fn call_twice<F: Fn(i32)>(f: F) que llama a f(2, 3) da E0057 con note: callable defined here, señalando Fn(i32). La restricción es la lista de parámetros. Solución: llama a f(2), o cambia la restricción a Fn(i32, i32) si de verdad necesitas dos valores (entonces la closure de cada llamador tiene que recibir dos).

Closures en una caja. let f: Box<dyn Fn(i32, i32) -> i32> = Box::new(|a, b| a + b); y luego f(1) da E0057 con note: implementation defined here. Solución: pasa los dos valores, f(1, 2).

Ordenar. v.sort_by(|a| a.cmp(a)) da E0593, “closure is expected to take 2 arguments, but it takes 1 argument” (se esperaba que la closure recibiera 2 argumentos, pero recibe 1). sort_by compara dos elementos a la vez. Solución: v.sort_by(|a, b| a.cmp(b)). Mira Ordenar y buscar.

Número correcto, tipo equivocado. nums.iter().map(|x: i32| x * 2) da E0631, “type mismatch in closure arguments” (los tipos de los argumentos de la closure no coinciden), porque .iter() entrega &i32. La ayuda de rustc sugiere |x: &i32|. La solución más sencilla: quita la anotación, |x| x * 2.

Desempaquetar algo que no es una tupla. v.iter().map(|(a, b)| a + b) sobre un simple Vec<i32> da E0308, “expected integer, found (_, _)” (se esperaba un entero y se encontró una tupla). Solución: usa |(a, b)| solo cuando los elementos de verdad son pares.

Comprueba lo aprendido

1. let double = |x: i32| x * 2; se llama como double(2, 3). ¿Qué informa rustc?

2. names.iter().zip(ages.iter()).map(|n, a| ...) no compila. ¿Cuál es la solución?

3. Tanto E0057 como E0061 dicen this function takes 2 arguments but 1 argument was supplied. ¿Cómo sabes que llamaste a una closure?

Puntuación: 0 / 3