Pular para o conteúdo

Erro E0057 do Rust: this function takes N arguments but M were supplied (closures)

O E0057 quer dizer que você chamou uma closure (função anônima) com o número errado de argumentos: de menos ou de mais. É a versão para closures do E0061, e a mensagem é quase igual. A correção costuma ser rápida: olhe o |...| da closure e passe um valor para cada parâmetro. Esta página também fala de dois parentes próximos que você vai encontrar com iteradores, o E0593 e o E0631, porque eles vêm da mesma confusão. Para os outros erros desta série, veja Erros do compilador Rust.

error[E0057]: this function takes 2 arguments but 1 argument was supplied
--> src/main.rs:3:17
|
3 | let total = add(1); // ❌ add quer dois 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 quer dois números
| +++++++++++

Em português: “esta função recebe 2 argumentos, mas foi passado 1”. O rótulo diz que falta o argumento nº 2, do tipo i32, e a nota aponta para onde a closure foi definida.

Este é o programa que causa o erro:

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

Argumentos de mais dão o mesmo código, com um rótulo “unexpected argument” (argumento inesperado) e uma ajuda “remove the extra argument” (remova o argumento extra):

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

Leia a mensagem como a de funções: a primeira linha conta (a closure recebe 2, você passou 1), o rótulo embaixo da sua chamada diz qual argumento falta ou sobra, e o help: mostra onde ele entra. /* i32 */ é só um comentário que serve de lugar reservado e diz o tipo. Coloque um valor de verdade ali.

E0057 ou E0061? A primeira linha é a mesma. É o note: que diferencia:

  • closure defined here: você chamou uma closure. Isso é o E0057.
  • callable defined here: você chamou um f genérico com um bound como F: Fn(i32). Também é o E0057.
  • function defined here: uma fn normal, um método ou um ponteiro de função (let f: fn(i32, i32) -> i32 = add;). Isso é o E0061.

De qualquer jeito, a correção é a mesma: faça a chamada bater com a lista de parâmetros.

A lista de parâmetros de uma closure é tão fixa quanto a de uma função. |a: i32, b: i32| recebe exatamente dois i32, todas as vezes. Não existem argumentos com valor padrão nem “passe menos se quiser”. Mesmo quando você não escreve os tipos, o rustc os descobre pelo corpo da closure e pelo primeiro uso, e a partir daí eles ficam travados.

Por baixo dos panos, uma closure implementa uma das traits Fn com uma lista fixa de tipos de argumento, como Fn(i32, i32) -> i32. O compilador confere cada chamada com essa lista, então uma chamada com a quantidade errada nunca chega ao seu programa rodando. Como as closures e os seus parâmetros funcionam está em Closures e em Traits de closures.

Correção 1: Faça a chamada bater com os parâmetros da closure

Seção intitulada “Correção 1: Faça a chamada bater com os parâmetros da closure”

Escolha esta quase sempre. Vá até o local do note:, conte os parâmetros entre as barras |...| e passe um valor para cada um. Se você tem mais valores do que parâmetros, chame a closure mais de uma vez:

fn main() {
let add = |a: i32, b: i32| a + b;
let greet = |name: &str| println!("Olá, {name}!");
let total = add(1, 2);
println!("{total}");
greet("Ferris");
greet("Corro"); // uma chamada por nome
}

Saída:

3
Olá, Ferris!
Olá, Corro!
▶ Experimente você mesmo

Se a closure realmente deveria receber outra quantidade de valores, mude a closure. Também não tem problema, desde que todas as chamadas concordem com ela.

Escolha esta quando houver tuplas no meio. |(name, score)| e |name, score| parecem iguais, mas são closures diferentes. A primeira recebe um argumento, uma tupla, e a desempacota. A segunda recebe dois argumentos.

Chamar uma closure de tupla com dois valores dá o E0057, e o rustc sugere colocar os parênteses:

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));
| + +

A ajuda diz: “coloque esses argumentos entre parênteses para montar uma tupla”.

O erro contrário acontece o tempo todo com iteradores. zip, enumerate e a iteração sobre mapas entregam à sua closure uma tupla por item. Escrever .map(|name, score| ...) dá outro código, o 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}"))

Ou seja: o map esperava uma closure que recebe uma única tupla de 2 elementos, mas a sua recebe 2 argumentos separados.

Os dois têm a mesma correção: coloque os parênteses onde está a tupla.

fn main() {
let names = vec!["Ferris", "Corro"];
let scores = vec![90, 75];
// zip entrega uma tupla (name, score) por item: desempacote na lista de parâmetros
let lines: Vec<String> = names
.iter()
.zip(scores.iter())
.map(|(name, score)| format!("{name}: {score}"))
.collect();
println!("{:?}", lines);
// uma closure que recebe uma tupla é chamada com uma tupla
let show = |(name, score): (&str, i32)| println!("{name} fez {score} pontos");
show(("Ferris", 90));
}

Saída:

["Ferris: 90", "Corro: 75"]
Ferris fez 90 pontos
▶ Experimente você mesmo

O mesmo vale para .enumerate().map(|(i, item)| ...) e map.iter().for_each(|(key, value)| ...). Veja Iteradores e Tuplas.

Correção 3: Adapte com uma closure intermediária

Seção intitulada “Correção 3: Adapte com uma closure intermediária”

Escolha esta quando você passa uma função ou closure para outra função (uma “função de ordem superior”) e as quantidades de parâmetros não batem. Aqui apply quer algo que receba um i32, mas add recebe dois. Isso dá o E0593 de novo:

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

Em português: “esperava-se uma função que recebe 1 argumento, mas ela recebe 2”.

Não mude o add. Embrulhe-o em uma pequena closure que tenha o formato que apply espera e que preencha sozinha o 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;
// recebe 1 argumento, chama add com 2
println!("{}", apply(|x| add(x, bonus), 5));
// recebe 1 tupla, chama add com as duas partes dela
let pairs = vec![(1, 2), (3, 4)];
let sums: Vec<i32> = pairs.into_iter().map(|(a, b)| add(a, b)).collect();
println!("{:?}", sums);
}

Saída:

15
[3, 7]
▶ Experimente você mesmo

A closure intermediária pega bonus emprestado do código ao redor, que é exatamente aquilo em que as closures são boas. Passar uma função pelo nome (apply(double, 5)) só funciona quando os parâmetros dela já batem. Veja Traits de closures.

Dentro de uma função genérica. Uma fn call_twice<F: Fn(i32)>(f: F) que chama f(2, 3) dá o E0057 com note: callable defined here, apontando para Fn(i32). O bound é a lista de parâmetros. Correção: chame f(2), ou mude o bound para Fn(i32, i32) se você realmente precisa de dois valores (aí a closure de cada chamador precisa receber dois).

Closures em Box. let f: Box<dyn Fn(i32, i32) -> i32> = Box::new(|a, b| a + b); e depois f(1) dá o E0057 com note: implementation defined here. Correção: passe os dois valores, f(1, 2).

Ordenação. v.sort_by(|a| a.cmp(a)) dá o E0593, “closure is expected to take 2 arguments, but it takes 1 argument”. O sort_by compara dois itens de cada vez. Correção: v.sort_by(|a, b| a.cmp(b)). Veja Ordenação.

Quantidade certa, tipo errado. nums.iter().map(|x: i32| x * 2) dá o E0631, “type mismatch in closure arguments” (tipos incompatíveis nos argumentos da closure), porque .iter() entrega &i32. A ajuda do rustc sugere |x: &i32|. A correção mais simples: tire a anotação, |x| x * 2.

Desempacotar algo que não é uma tupla. v.iter().map(|(a, b)| a + b) em um simples Vec<i32> dá o E0308, “expected integer, found (_, _)”. Correção: só use |(a, b)| quando os itens forem pares de verdade.

Teste o que aprendeu

1. let double = |x: i32| x * 2; é chamada como double(2, 3). O que o rustc mostra?

2. names.iter().zip(ages.iter()).map(|n, a| ...) não compila. Qual é a correção?

3. Tanto o E0057 quanto o E0061 dizem this function takes 2 arguments but 1 argument was supplied. Como saber que você chamou uma closure?

Pontuação: 0 / 3