Pular para o conteúdo

Erro E0061 do Rust: this function takes N arguments but M were supplied

O E0061 quer dizer que você chamou uma função com o número errado de argumentos: de menos ou de mais. É um dos erros mais amigáveis do Rust, porque o rustc te diz exatamente qual argumento falta ou sobra, e de que tipo ele deveria ser. Ele aparece muito para quem vem de Python, JavaScript ou C#, onde argumentos com valor padrão e sobrecarga são coisas normais. O Rust não tem nenhum dos dois, e esta página mostra o que fazer no lugar. Para os outros erros desta série, veja Erros do compilador Rust.

error[E0061]: this function takes 2 arguments but 1 argument was supplied
--> src/main.rs:6:5
|
6 | greet("Ferris"); // ❌ greet quer dois argumentos
| ^^^^^---------- 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 quer dois argumentos
| ++++++++++++

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 &str.

Este é o programa que causa o erro:

fn greet(name: &str, greeting: &str) {
println!("{greeting}, {name}!");
}
fn main() {
greet("Ferris"); // ❌ greet quer dois argumentos
}

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[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}`

Para um método chamado com ponto, a primeira linha diz “this method takes…” (este método recebe…). Para uma tuple struct como Rgb(255, 0), ela diz “this struct takes 3 arguments”. É sempre o mesmo erro.

A mensagem tem três partes, e cada uma ajuda:

  1. A primeira linha conta: a função quer 2 argumentos, você passou 1.
  2. O rótulo embaixo da sua chamada diz qual é o problema: “argument #2 of type &str is missing” (falta o argumento nº 2, do tipo &str).
  3. O note: pula para a definição da função e sublinha o parâmetro que você deixou de fora, greeting: &str.

A linha help: mostra onde o argumento entra. /* &str */ é só um lugar reservado que diz o tipo; é um comentário, então colar do jeito que está não compila. Coloque um valor de verdade ali, como "Olá".

É como uma máquina de café que precisa de um copo e de uma moeda. Colocar só a moeda não te dá meio café. Ela simplesmente não liga.

No Rust, a assinatura de uma função é um contrato exato: estes parâmetros, nesta ordem, com estes tipos. Toda chamada precisa bater com ela. Não existem argumentos com valor padrão (greeting = "Hello"), nem sobrecarga (duas funções chamadas greet com parâmetros diferentes), nem funções que recebem “quantos argumentos você quiser”. Tentar escrever um valor padrão dá error: parameter defaults are not supported, e um segundo greet dá o E0428, “the name greet is defined multiple times” (o nome greet foi definido mais de uma vez).

Parece rígido, mas vale a pena. Quando você lê greet(name, greeting), sabe exatamente qual função roda e o que ela recebe. Quando você adiciona um parâmetro a uma função, o compilador lista todas as chamadas que precisam ser atualizadas, e nenhuma fica para trás. (Coisas como println!, que recebem qualquer número de valores, são macros, e é isso que o ! quer dizer. Veja Macros.)

Como os parâmetros funcionam está explicado em Funções.

Correção 1: Passe exatamente o que a assinatura pede

Seção intitulada “Correção 1: Passe exatamente o que a assinatura pede”

Escolha esta quase sempre. Abra o local do note:, leia a lista de parâmetros e passe um valor por parâmetro, na ordem:

fn greet(name: &str, greeting: &str) {
println!("{greeting}, {name}!");
}
fn main() {
greet("Ferris", "Olá");
greet("Corro", "Bom dia");
}

Saída:

Olá, Ferris!
Bom dia, Corro!
▶ Experimente você mesmo

Se você acabou de mudar a função e agora todas as chamadas quebraram, é o compilador te ajudando: corrija cada chamada da lista, ou pense se o parâmetro novo não deveria ser opcional (Correção 3).

Escolha esta quando a função tem um parâmetro self. Um método normalmente é chamado como valor.metodo(args). O valor antes do ponto preenche o self, então você não o conta. Se você escreve Type::method(args), nada preenche o 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);
| +++++++++++++++++++

Aqui falta o argumento nº 1, do tipo &mut Account: justamente o self.

Repare na contagem. Chamando como Account::deposit(...), o rustc diz que a função recebe 2 argumentos, porque &mut self conta. Chamando como acc.deposit(), ele diz que o método recebe 1 argumento, porque o ponto já fornece o self. As duas formas funcionam quando todos os argumentos estão lá:

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); // o jeito de sempre
Account::deposit(&mut acc, 25); // a mesma chamada, escrita por extenso
println!("Saldo: {}", acc.balance);
}

Saída:

Saldo: 75
▶ Experimente você mesmo

No código do dia a dia, use a forma com ponto. Type::method(&x, a) é útil quando você precisa passar o próprio método para algum lugar, como team.iter().map(Player::label) (exemplo). Se uma função não tem self nenhum (como new), ela só pode ser chamada como Type::function(...). Veja Structs e o E0424.

Correção 3: Use Option<T> para um argumento que às vezes não é necessário

Seção intitulada “Correção 3: Use Option<T> para um argumento que às vezes não é necessário”

Escolha esta quando um argumento costuma ter um valor padrão razoável. Transforme-o em um Option, passe None para “use o padrão” e preencha o valor com unwrap_or:

fn connect(host: &str, port: u16, timeout_secs: Option<u64>) {
let timeout = timeout_secs.unwrap_or(30);
println!("Conectando a {host}:{port} (timeout de {timeout}s)");
}
fn main() {
connect("localhost", 8080, None);
connect("example.com", 443, Some(5));
}

Saída:

Conectando a localhost:8080 (timeout de 30s)
Conectando a example.com:443 (timeout de 5s)
▶ Experimente você mesmo

Sendo sincero: quem chama ainda escreve o argumento, None. O Rust prefere assim, porque a chamada mostra que um valor padrão está sendo usado. Se você prefere não escrever nada, crie duas funções com nomes claros, como connect(host, port), que chama connect_with_timeout(host, port, 30). Essa é a resposta habitual do Rust para a sobrecarga. Veja Option.

Correção 4: Use uma struct de configuração com Default

Seção intitulada “Correção 4: Use uma struct de configuração com Default”

Escolha esta quando uma função tem muitas configurações e a maioria dos chamadores só muda uma ou duas. Coloque as configurações em uma struct, implemente Default e deixe quem chama preencher só o que precisa com ..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!(
"Escutando em {}:{} (timeout de {}s, verbose: {})",
config.host, config.port, config.timeout_secs, config.verbose
);
}
fn main() {
start(ServerConfig::default());
start(ServerConfig { port: 3000, verbose: true, ..Default::default() });
}

Saída:

Escutando em localhost:8080 (timeout de 30s, verbose: false)
Escutando em localhost:3000 (timeout de 30s, verbose: true)
▶ Experimente você mesmo

Isso também te dá algo parecido com argumentos nomeados: port: 3000 diz o que o número significa. Se o padrão de cada campo for o próprio padrão do tipo (0, false, string vazia), #[derive(Default)] escreve o impl para você. Para APIs maiores, o próximo passo é o padrão builder (Server::builder().port(3000).build()). Veja Structs e Padrões de projeto.

Depois de adicionar um parâmetro. Você dá um segundo parâmetro a fn save(path: &str), e toda chamada antiga save("notes.txt") quebra com o E0061. Correção: atualize cada chamada que o compilador lista, ou use a Correção 3 ou a 4 se o valor novo costuma ser sempre o mesmo.

Métodos da biblioteca padrão. names.push("Cy", "Di") dá “this method takes 1 argument but 2 arguments were supplied”. O push adiciona um item. Correção: chame duas vezes, ou use names.extend(["Cy", "Di"]). Veja Vetores.

Tuple structs. struct Rgb(u8, u8, u8); e depois Rgb(255, 0) dá “this struct takes 3 arguments but 2 arguments were supplied”. Correção: passe todos os campos, Rgb(255, 0, 0).

Costumes de outras linguagens. O greet("Ferris", greeting="Hi") do Python não é sintaxe de Rust. Escrever greet(greeting = "Hi") não dá nome a um argumento; o rustc lê isso como uma atribuição a uma variável chamada greeting e também mostra o E0425. Correção: passe os valores pela posição, ou use uma struct de configuração (Correção 4).

Closures. Chamar add(1) quando add é |a: i32, b: i32| a + b dá a mesma mensagem, mas com o código E0057 (a versão para closures). A correção é a mesma: passe os dois valores.

  • E0308: a quantidade de argumentos está certa, mas um deles tem o tipo errado, muitas vezes porque dois foram trocados de lugar.
  • E0424: self usado em uma função sem parâmetro self, o outro lado da diferença entre método e função associada.
  • E0599: não existe método com esse nome, por exemplo ao chamar com ponto uma função associada como new.
  • E0425: não foi possível encontrar um valor, o que pode aparecer junto com o E0061 quando você tenta usar argumentos nomeados.
  • Todos os erros do compilador: a lista completa de páginas de erros. Também relacionados: E0057 (closure chamada com o número errado de argumentos) e E0428 (uma função definida duas vezes, ao tentar fazer sobrecarga), que não tem página própria.

Teste o que aprendeu

1. fn area(w: u32, h: u32) -> u32 é chamada como area(5). O que o rustc mostra?

2. Um método é fn deposit(&mut self, amount: i64). Qual chamada está certa?

3. Você quer um argumento timeout com o qual a maioria dos chamadores não se importa. Qual é uma abordagem comum em Rust?

Pontuação: 0 / 3