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.
El mensaje de error
Sección titulada «El mensaje de error»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 hereQué significa
Sección titulada «Qué significa»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 unfgenérico con una restricción comoF: Fn(i32). También es E0057.function defined here(función definida aquí): unafnnormal, 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.
Por qué Rust hace esto
Sección titulada «Por qué Rust hace esto»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.
Cómo solucionarlo
Sección titulada «Cómo solucionarlo»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!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 herehelp: 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 puntosLo 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 callEn 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]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.
Dónde lo verás normalmente
Sección titulada «Dónde lo verás normalmente»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.
Errores relacionados
Sección titulada «Errores relacionados»- E0061: número equivocado de argumentos: el mismo mensaje para funciones normales, métodos y punteros a función.
- E0308: mismatched types: el número es correcto, pero un argumento (o un patrón de tupla) tiene el tipo equivocado.
- E0373: closure may outlive the current function: otro error común con closures, sobre variables capturadas en hilos y closures devueltas.
- E0599: no method found: puede aparecer junto a E0631, porque una closure con la firma equivocada rompe el resto de una cadena de iteradores.
- E0593: closure takes 2 distinct arguments: una closure o función pasada con un número equivocado de parámetros.
- E0631: type mismatch in closure arguments: el número de parámetros es correcto, pero el tipo no.
- Todos los errores del compilador: la lista completa de páginas de errores.
Comprueba lo que aprendiste
Sección titulada «Comprueba lo que aprendiste»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
Para leer más
Sección titulada «Para leer más»- E0057 — Rust error codes index (en inglés): la explicación oficial de este error.
- Closures — The Rust Book (en inglés): la sintaxis de las closures, la inferencia de tipos de sus parámetros y los traits
Fn. - Closures — Rust by Example (en inglés): ejemplos cortos y ejecutables de closures, incluido cómo pasarlas a funciones.
std::ops::Fn— documentación de la biblioteca estándar (en inglés): cómo las restriccionesFn(i32) -> i32describen los parámetros de una closure.- E0593 — Rust error codes index (en inglés): la explicación oficial del error de pasar una closure con un número equivocado de parámetros.