Ошибка 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». Каждый раз это одна и та же ошибка.
Что это значит
Заголовок раздела «Что это значит»В сообщении три части, и каждая помогает:
- Первая строка считает: функции нужно 2 аргумента, ты передал 1.
- Подпись под вызовом называет проблему: «argument #2 of type
&stris missing» (нет аргумента №2 типа&str). note:переносит тебя к определению функции и подчёркивает пропущенный параметр,greeting: &str.
Строка help: показывает, куда поставить аргумент. /* &str */ — всего лишь заглушка, которая называет тип; это комментарий, так что если вставить его как есть, код не скомпилируется. Поставь туда настоящее значение, например "Привет".
Это как кофейный автомат, которому нужны и стаканчик, и монета. Если бросить только монету, полкофе ты не получишь. Он просто не включится.
Почему Rust так делает
Заголовок раздела «Почему Rust так делает»В 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).
Исправление 2: вызывай методы через точку
Заголовок раздела «Исправление 2: вызывай методы через точку»Выбирай это, когда у функции есть параметр 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
Что почитать
Заголовок раздела «Что почитать»- E0061 — список кодов ошибок Rust (на английском): официальное объяснение этой ошибки.
- Функции — книга «Язык программирования Rust» (перевод на русский): параметры, типы в сигнатурах и то, как работают вызовы.
- Синтаксис метода — книга «Язык программирования Rust» (перевод на русский): почему
selfзаполняется точкой, и ассоциированные функции вродеnew. - Трейт
Default— документация стандартной библиотеки (на английском): выводDefaultи использование..Default::default(). - Builder pattern — Rust Design Patterns (на английском): привычная замена длинным спискам аргументов и необязательным аргументам.