Перейти к содержимому

Ошибка Rust E0057: this function takes N arguments but M were supplied (замыкания)

E0057 значит, что ты вызвал замыкание с неверным числом аргументов: слишком мало или слишком много. Это версия E0061 для замыканий, и сообщение читается почти так же. Обычно исправление быстрое: посмотри на |...| замыкания и передай по одному значению на каждый параметр. Ещё на этой странице разобраны две близкие родственницы, которые ты встретишь с итераторами, — E0593 и E0631: они возникают из-за той же путаницы. Другие ошибки из этой серии собраны на странице Ошибки компилятора Rust.

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

Компилятор пишет: замыкание принимает 2 аргумента, а передан 1; не хватает аргумента №2 типа i32. Вот программа, которая выдаёт эту ошибку:

fn main() {
let add = |a: i32, b: i32| a + b;
let total = add(1); // ❌ add wants two numbers
println!("{total}");
}

Комментарий говорит: add хочет два числа. Слишком много аргументов дают тот же код, но с подписью «unexpected argument» (неожиданный аргумент) и подсказкой «remove the extra argument» (убери лишний аргумент):

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

Читай сообщение так же, как для функций: первая строка считает (замыкание принимает 2, ты передал 1), подпись под вызовом называет недостающий или лишний аргумент, а help: показывает, куда его поставить. /* i32 */ — всего лишь комментарий-заглушка, который называет тип. Поставь туда настоящее значение.

E0057 или E0061? Первая строка одинаковая. Различить их помогает note::

  • closure defined here: ты вызвал замыкание. Это E0057.
  • callable defined here: ты вызвал обобщённый f с ограничением вроде F: Fn(i32). Тоже E0057.
  • function defined here: обычная fn, метод или указатель на функцию (let f: fn(i32, i32) -> i32 = add;). Это E0061.

В любом случае исправление одно: приведи вызов в соответствие со списком параметров.

Список параметров замыкания так же неизменен, как у функции. |a: i32, b: i32| принимает ровно два i32, каждый раз. Нет аргументов по умолчанию и нет «можно передать поменьше». Даже если ты не пишешь типы, rustc выводит их из тела замыкания и из первого использования, а дальше они закреплены.

Внутри замыкание реализует один из трейтов Fn с фиксированным списком типов аргументов, например Fn(i32, i32) -> i32. Компилятор сверяет с этим списком каждый вызов, так что вызов с неверным числом аргументов никогда не доберётся до работающей программы. Как устроены замыкания и их параметры, рассказано в уроках Замыкания и Fn, FnMut и FnOnce.

Исправление 1: приведи вызов в соответствие с параметрами замыкания

Заголовок раздела «Исправление 1: приведи вызов в соответствие с параметрами замыкания»

Выбирай это почти всегда. Перейди к месту из note:, посчитай параметры между вертикальными чертами |...| и передай по одному значению на каждый. Если значений больше, чем параметров, вызови замыкание несколько раз:

fn main() {
let add = |a: i32, b: i32| a + b;
let greet = |name: &str| println!("Привет, {name}!");
let total = add(1, 2);
println!("{total}");
greet("Ferris");
greet("Corro"); // по одному вызову на каждое имя
}

Вывод:

3
Привет, Ferris!
Привет, Corro!
▶ Попробуй сам

Если замыкание на самом деле должно принимать другое число значений, поменяй само замыкание. Это тоже нормально, лишь бы каждый вызов ему соответствовал.

Исправление 2: кортеж считается одним аргументом

Заголовок раздела «Исправление 2: кортеж считается одним аргументом»

Выбирай это, когда в деле замешаны кортежи. |(name, score)| и |name, score| похожи, но это разные замыкания. Первое принимает один аргумент — кортеж — и разбирает его. Второе принимает два аргумента.

Если вызвать замыкание с кортежем, передав два значения, получишь E0057, и rustc предложит добавить скобки:

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

Подсказка говорит: оберни аргументы в скобки, чтобы получился кортеж. Обратная ошибка постоянно случается с итераторами. zip, enumerate и перебор хеш-таблицы передают замыканию один кортеж на элемент. Если написать .map(|name, score| ...), получишь другой код, 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}"))

Здесь компилятор ждёт замыкание, которое принимает один кортеж из двух элементов, а твоё принимает два отдельных аргумента. Исправление у обеих ошибок одно: поставь скобки там, где кортеж.

fn main() {
let names = vec!["Ferris", "Corro"];
let scores = vec![90, 75];
// zip даёт один кортеж (name, score) на элемент: разбери его в списке параметров
let lines: Vec<String> = names
.iter()
.zip(scores.iter())
.map(|(name, score)| format!("{name}: {score}"))
.collect();
println!("{:?}", lines);
// замыкание, которое принимает кортеж, вызывают с одним кортежем
let show = |(name, score): (&str, i32)| println!("{name} набрал {score}");
show(("Ferris", 90));
}

Вывод:

["Ferris: 90", "Corro: 75"]
Ferris набрал 90
▶ Попробуй сам

То же самое для .enumerate().map(|(i, item)| ...) и map.iter().for_each(|(key, value)| ...). Подробнее в уроках Итераторы и Кортежи.

Исправление 3: подстройся через замыкание-обёртку

Заголовок раздела «Исправление 3: подстройся через замыкание-обёртку»

Выбирай это, когда передаёшь функцию или замыкание в другую функцию («функцию высшего порядка»), а число параметров не совпадает. Здесь apply ждёт что-то, что принимает один i32, а add принимает два. Это снова 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

Компилятор ждёт функцию с одним аргументом, а add принимает два. Не меняй add. Оберни её в маленькое замыкание той формы, которую ждёт apply, и пусть оно само подставит недостающее значение:

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;
// принимает 1 аргумент, вызывает add с двумя
println!("{}", apply(|x| add(x, bonus), 5));
// принимает 1 кортеж, вызывает add с двумя его частями
let pairs = vec![(1, 2), (3, 4)];
let sums: Vec<i32> = pairs.into_iter().map(|(a, b)| add(a, b)).collect();
println!("{:?}", sums);
}

Вывод:

15
[3, 7]
▶ Попробуй сам

Обёртка заимствует bonus из окружающего кода — как раз то, в чём замыкания хороши. Передавать функцию по имени (apply(double, 5)) можно, только когда её параметры уже подходят. Подробнее в уроке Fn, FnMut и FnOnce.

Внутри обобщённой функции. fn call_twice<F: Fn(i32)>(f: F), которая вызывает f(2, 3), даёт E0057 с note: callable defined here, указывающим на Fn(i32). Ограничение и есть список параметров. Исправление: вызови f(2) или смени ограничение на Fn(i32, i32), если тебе действительно нужны два значения (тогда замыкание у каждого вызывающего кода должно принимать два).

Замыкания в Box. let f: Box<dyn Fn(i32, i32) -> i32> = Box::new(|a, b| a + b);, а затем f(1) даёт E0057 с note: implementation defined here. Исправление: передай оба значения, f(1, 2).

Сортировка. v.sort_by(|a| a.cmp(a)) даёт E0593, «closure is expected to take 2 arguments, but it takes 1 argument». sort_by сравнивает по два элемента за раз. Исправление: v.sort_by(|a, b| a.cmp(b)). Подробнее в уроке Сортировка.

Число верное, тип нет. nums.iter().map(|x: i32| x * 2) даёт E0631, «type mismatch in closure arguments», потому что .iter() выдаёт &i32. Подсказка rustc предлагает |x: &i32|. Самое простое исправление — убрать аннотацию: |x| x * 2.

Разбор того, что не является кортежем. v.iter().map(|(a, b)| a + b) для обычного Vec<i32> даёт E0308, «expected integer, found (_, _)». Исправление: пиши |(a, b)|, только когда элементы действительно пары.

Проверь себя

1. let double = |x: i32| x * 2; вызывают как double(2, 3). Что сообщит rustc?

2. names.iter().zip(ages.iter()).map(|n, a| ...) не компилируется. Как исправить?

3. И E0057, и E0061 говорят this function takes 2 arguments but 1 argument was supplied. Как понять, что ты вызвал замыкание?

Результат: 0 / 3