Ошибка 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.
В любом случае исправление одно: приведи вызов в соответствие со списком параметров.
Почему Rust так делает
Заголовок раздела «Почему Rust так делает»Список параметров замыкания так же неизменен, как у функции. |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 herehelp: 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)|, только когда элементы действительно пары.
Связанные ошибки
Заголовок раздела «Связанные ошибки»- E0061: wrong number of arguments: то же сообщение для обычных функций, методов и указателей на функции.
- E0308: mismatched types: число верное, но у аргумента (или образца-кортежа) не тот тип.
- E0373: closure may outlive the current function: ещё одна частая ошибка замыканий — о захваченных переменных в потоках и возвращаемых замыканиях.
- E0599: no method found: может появиться рядом с E0631, потому что замыкание с неверной сигнатурой ломает остаток цепочки итераторов.
- E0593: closure takes 2 distinct arguments: замыкание или функция переданы с неверным числом параметров.
- E0631: type mismatch in closure arguments: число параметров верное, но тип не тот.
- Все ошибки компилятора: полный список страниц с ошибками.
Проверь себя
Заголовок раздела «Проверь себя»Проверь себя
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
Что почитать
Заголовок раздела «Что почитать»- E0057 — список кодов ошибок Rust (на английском): официальное объяснение этой ошибки.
- Замыкания: анонимные функции, которые захватывают своё окружение — книга «Язык программирования Rust» (перевод на русский): синтаксис замыканий, вывод типов параметров и трейты
Fn. - Замыкания — Rust на примерах (перевод на русский): короткие запускаемые примеры замыканий, в том числе их передачи в функции.
std::ops::Fn— документация стандартной библиотеки (на английском): как ограничения видаFn(i32) -> i32описывают параметры замыкания.- E0593 — список кодов ошибок Rust (на английском): официальное объяснение ошибки, когда замыкание передано с неверным числом параметров.