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

Ошибка Rust E0061: this function takes N arguments but M were supplied

E0061 значит, что ты вызвал функцию с неверным числом аргументов: слишком мало или слишком много. Это одна из самых дружелюбных ошибок в Rust, потому что rustc точно говорит, какого аргумента не хватает или какой лишний и какого типа он должен быть. Она часто встречается у тех, кто пришёл из Python, JavaScript или C#, где аргументы по умолчанию и перегрузка — обычное дело. В Rust нет ни того, ни другого, и на этой странице показано, что делать вместо них. Другие ошибки из этой серии собраны на странице Ошибки компилятора Rust.

error[E0061]: this function takes 2 arguments but 1 argument was supplied
--> src/main.rs:6:5
|
6 | greet("Ferris"); // ❌ greet wants two arguments
| ^^^^^---------- argument #2 of type `&str` is missing
|
note: function defined here
--> src/main.rs:1:4
|
1 | fn greet(name: &str, greeting: &str) {
| ^^^^^ --------------
help: provide the argument
|
6 | greet("Ferris", /* &str */); // ❌ greet wants two arguments
| ++++++++++++

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

fn greet(name: &str, greeting: &str) {
println!("{greeting}, {name}!");
}
fn main() {
greet("Ferris"); // ❌ greet wants two arguments
}

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

error[E0061]: this function takes 2 arguments but 3 arguments were supplied
--> src/main.rs:6:13
|
6 | let a = area(3, 4, 5);
| ^^^^ - unexpected argument #3 of type `{integer}`

Для метода, вызванного через точку, первая строка вместо этого говорит «this method takes…». Для кортежной структуры вроде Rgb(255, 0) — «this struct takes 3 arguments». Каждый раз это одна и та же ошибка.

В сообщении три части, и каждая помогает:

  1. Первая строка считает: функции нужно 2 аргумента, ты передал 1.
  2. Подпись под вызовом называет проблему: «argument #2 of type &str is missing» (нет аргумента №2 типа &str).
  3. note: переносит тебя к определению функции и подчёркивает пропущенный параметр, greeting: &str.

Строка help: показывает, куда поставить аргумент. /* &str */ — всего лишь заглушка, которая называет тип; это комментарий, так что если вставить его как есть, код не скомпилируется. Поставь туда настоящее значение, например "Привет".

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

В Rust сигнатура функции — точный договор: вот эти параметры, в этом порядке, с этими типами. Каждый вызов обязан ему соответствовать. Нет аргументов по умолчанию (greeting = "Hello"), нет перегрузки (двух функций greet с разными параметрами) и нет функций, которые принимают «сколько угодно» аргументов. Попытка написать значение по умолчанию даёт error: parameter defaults are not supported, а вторая greet — E0428, «the name greet is defined multiple times».

Звучит строго, но это окупается. Когда ты читаешь greet(name, greeting), ты точно знаешь, какая функция запустится и что она получит. Когда ты добавляешь функции параметр, компилятор перечисляет все вызовы, которые нужно обновить, и ни один не потеряется. (Вещи вроде println!, которые и правда принимают сколько угодно значений, — это макросы, именно это и значит !. Подробнее в уроке Макросы.)

Как работают параметры, рассказано в уроке Функции.

Исправление 1: передай ровно то, что просит сигнатура

Заголовок раздела «Исправление 1: передай ровно то, что просит сигнатура»

Выбирай это почти всегда. Открой место из note:, прочитай список параметров и передай по одному значению на каждый параметр, по порядку:

fn greet(name: &str, greeting: &str) {
println!("{greeting}, {name}!");
}
fn main() {
greet("Ferris", "Привет");
greet("Corro", "Доброе утро");
}

Вывод:

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

Если ты только что изменил функцию и теперь сломан каждый вызов, это компилятор тебе помогает: исправь все вызовы, которые он перечислил, или спроси себя, не должен ли новый параметр быть необязательным (исправление 3).

Выбирай это, когда у функции есть параметр self. Метод обычно вызывают как value.method(args). Значение перед точкой заполняет self, поэтому его не считают. Если вместо этого написать Type::method(args), self заполнить нечем:

error[E0061]: this function takes 2 arguments but 1 argument was supplied
--> src/main.rs:13:5
|
13 | Account::deposit(50);
| ^^^^^^^^^^^^^^^^ -- argument #1 of type `&mut Account` is missing
|
help: provide the argument
|
13 | Account::deposit(/* &mut Account */, 50);
| +++++++++++++++++++

Здесь не хватает аргумента №1 типа &mut Account. Обрати внимание на подсчёт. При вызове Account::deposit(...) rustc говорит, что функция принимает 2 аргумента, потому что &mut self тоже считается. При вызове acc.deposit() он говорит, что метод принимает 1 аргумент, потому что точка уже передаёт self. Обе формы работают, когда все аргументы на месте:

struct Account {
balance: i64,
}
impl Account {
fn deposit(&mut self, amount: i64) {
self.balance += amount;
}
}
fn main() {
let mut acc = Account { balance: 0 };
acc.deposit(50); // обычный способ
Account::deposit(&mut acc, 25); // тот же вызов, написанный полностью
println!("Баланс: {}", acc.balance);
}

Вывод:

Баланс: 75
▶ Попробуй сам

В обычном коде пользуйся формой с точкой. Type::method(&x, a) полезна, когда нужно куда-то передать сам метод, например team.iter().map(Player::label) (пример). Если у функции вообще нет self (как у new), её можно вызвать только как Type::function(...). Подробнее в уроке Структуры и на странице E0424.

Исправление 3: возьми Option<T> для аргумента, который нужен не всегда

Заголовок раздела «Исправление 3: возьми Option<T> для аргумента, который нужен не всегда»

Выбирай это, когда у одного аргумента обычно есть разумное значение по умолчанию. Сделай его Option, передавай None, чтобы сказать «возьми значение по умолчанию», и подставляй его через unwrap_or:

fn connect(host: &str, port: u16, timeout_secs: Option<u64>) {
let timeout = timeout_secs.unwrap_or(30);
println!("Подключаюсь к {host}:{port} (тайм-аут {timeout} с)");
}
fn main() {
connect("localhost", 8080, None);
connect("example.com", 443, Some(5));
}

Вывод:

Подключаюсь к localhost:8080 (тайм-аут 30 с)
Подключаюсь к example.com:443 (тайм-аут 5 с)
▶ Попробуй сам

Честно говоря, вызывающий код всё равно пишет аргумент — None. Rust так и предпочитает: по вызову видно, что используется значение по умолчанию. Если не хочешь писать его вовсе, сделай две функции с понятными именами, например connect(host, port), которая вызывает connect_with_timeout(host, port, 30). Это обычный ответ Rust на перегрузку. Подробнее в уроке Option.

Исправление 4: возьми структуру настроек с Default

Заголовок раздела «Исправление 4: возьми структуру настроек с Default»

Выбирай это, когда у функции много настроек, а большинство вызовов меняет только одну-две. Сложи настройки в структуру, реализуй Default и дай вызывающему коду заполнить только нужное через ..Default::default():

struct ServerConfig {
host: String,
port: u16,
timeout_secs: u64,
verbose: bool,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
host: String::from("localhost"),
port: 8080,
timeout_secs: 30,
verbose: false,
}
}
}
fn start(config: ServerConfig) {
println!(
"Слушаю {}:{} (тайм-аут {} с, verbose: {})",
config.host, config.port, config.timeout_secs, config.verbose
);
}
fn main() {
start(ServerConfig::default());
start(ServerConfig { port: 3000, verbose: true, ..Default::default() });
}

Вывод:

Слушаю localhost:8080 (тайм-аут 30 с, verbose: false)
Слушаю localhost:3000 (тайм-аут 30 с, verbose: true)
▶ Попробуй сам

Заодно получаются почти именованные аргументы: port: 3000 говорит, что значит это число. Если значение по умолчанию у каждого поля — стандартное для его типа (0, false, пустая строка), #[derive(Default)] напишет impl за тебя. Для API побольше следующий шаг — шаблон «строитель» (builder pattern): Server::builder().port(3000).build(). Подробнее в уроках Структуры и Шаблоны проектирования.

После добавления параметра. Ты даёшь fn save(path: &str) второй параметр, и каждый старый вызов save("notes.txt") ломается с E0061. Исправление: обнови все вызовы, которые перечислил компилятор, или используй исправление 3 или 4, если новое значение обычно одно и то же.

Методы стандартной библиотеки. names.push("Cy", "Di") даёт «this method takes 1 argument but 2 arguments were supplied». push добавляет один элемент. Исправление: вызови его дважды или используй names.extend(["Cy", "Di"]). Подробнее в уроке Векторы.

Кортежные структуры. struct Rgb(u8, u8, u8);, а потом Rgb(255, 0) даёт «this struct takes 3 arguments but 2 arguments were supplied». Исправление: передай все поля, Rgb(255, 0, 0).

Привычки из других языков. greet("Ferris", greeting="Hi") из Python — это не синтаксис Rust. greet(greeting = "Hi") не называет аргумент по имени: rustc читает это как присваивание переменной greeting и вдобавок сообщает E0425. Исправление: передавай значения по позиции или используй структуру настроек (исправление 4).

Замыкания. Вызов add(1), когда add — это |a: i32, b: i32| a + b, даёт то же сообщение, но под кодом E0057 (версия для замыканий). Исправление то же: передай оба значения.

  • E0308: число аргументов верное, но у одного не тот тип — часто потому, что два аргумента перепутаны местами.
  • E0424: self в функции без параметра self — другая сторона темы «метод или ассоциированная функция».
  • E0599: метода с таким именем нет, например когда ассоциированную функцию вроде new вызывают через точку.
  • E0425: не найдено значение; может появиться рядом с E0061, когда пробуешь именованные аргументы.
  • Все ошибки компилятора: полный список страниц с ошибками. Ещё по теме: E0057 (замыкание вызвано с неверным числом аргументов) и E0428 (функция определена дважды при попытке перегрузки).

Проверь себя

1. fn area(w: u32, h: u32) -> u32 вызывают как area(5). Что сообщит rustc?

2. Метод объявлен как fn deposit(&mut self, amount: i64). Какой вызов правильный?

3. Тебе нужен аргумент timeout, который большинству вызовов не важен. Как обычно поступают в Rust?

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