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.
A mensagem de erro
Seção intitulada “A mensagem de erro”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.
O que significa
Seção intitulada “O que significa”A mensagem tem três partes, e cada uma ajuda:
- A primeira linha conta: a função quer 2 argumentos, você passou 1.
- O rótulo embaixo da sua chamada diz qual é o problema: “argument #2 of type
&stris missing” (falta o argumento nº 2, do tipo&str). - 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.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”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.
Como corrigir
Seção intitulada “Como corrigir”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!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).
Correção 2: Chame métodos com ponto
Seção intitulada “Correção 2: Chame métodos com ponto”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: 75No 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)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)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.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”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.
Erros relacionados
Seção intitulada “Erros relacionados”- E0308: a quantidade de argumentos está certa, mas um deles tem o tipo errado, muitas vezes porque dois foram trocados de lugar.
- E0424:
selfusado em uma função sem parâmetroself, 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
Seção intitulada “Teste o que aprendeu”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
Para saber mais
Seção intitulada “Para saber mais”- E0061 — Rust error codes index (em inglês): a explicação oficial deste erro.
- Functions — The Rust Book (em inglês): parâmetros, tipos nas assinaturas e como as chamadas funcionam.
- Method Syntax — The Rust Book (em inglês): por que o
selfé preenchido pelo ponto, e funções associadas comonew. Defaulttrait — standard library docs (em inglês): como derivarDefaulte usar..Default::default().- Builder pattern — Rust Design Patterns (em inglês): a alternativa comum a listas longas de argumentos e a argumentos opcionais.