Salta ai contenuti

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

E0061 significa che hai chiamato una funzione con il numero sbagliato di argomenti: troppo pochi o troppi. È uno degli errori più gentili di Rust, perché rustc ti dice esattamente quale argomento manca o avanza, e di che tipo dovrebbe essere. Capita spesso a chi arriva da Python, JavaScript o C#, dove gli argomenti predefiniti e l’overloading sono normali. Rust non ha né gli uni né l’altro, e questa pagina ti mostra cosa fare al loro posto. Per gli altri errori di questa serie, vedi Errori del compilatore 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
| ++++++++++++

Il compilatore dice: «questa funzione prende 2 argomenti, ma ne è stato fornito 1». Questo è il programma che lo produce:

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

Troppi argomenti danno lo stesso codice, con un’etichetta «unexpected argument» (argomento inatteso) e un help «remove the extra argument» (togli l’argomento in più):

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

Per un metodo chiamato con il punto, la prima riga dice invece «this method takes…». Per una tuple struct come Rgb(255, 0) dice «this struct takes 3 arguments». È sempre lo stesso errore.

Il messaggio ha tre parti, e ognuna ti aiuta:

  1. La prima riga conta: la funzione vuole 2 argomenti, tu ne hai dato 1.
  2. L’etichetta sotto la tua chiamata dice qual è il problema: «argument #2 of type &str is missing» (manca l’argomento n. 2, di tipo &str).
  3. La note: salta alla definizione della funzione e sottolinea il parametro che hai tralasciato, greeting: &str.

La riga help: mostra dove va l’argomento. /* &str */ è solo un segnaposto che ti dice il tipo; è un commento, quindi se lo incolli così com’è non compila. Mettici un valore vero, come "Ciao".

È come una macchinetta del caffè che vuole sia il bicchiere sia la moneta. Se metti solo la moneta non ottieni mezzo caffè. Semplicemente non parte.

In Rust la firma di una funzione è un contratto preciso: questi parametri, in quest’ordine, con questi tipi. Ogni chiamata deve rispettarlo. Non ci sono argomenti predefiniti (greeting = "Hello"), non c’è overloading (due funzioni greet con parametri diversi) e non ci sono funzioni che accettano un numero «qualsiasi» di argomenti. Se provi a scrivere un valore predefinito ottieni error: parameter defaults are not supported, e un secondo greet dà E0428, «the name greet is defined multiple times».

Sembra rigido, ma ripaga. Quando leggi greet(name, greeting), sai esattamente quale funzione viene eseguita e cosa riceve. Quando aggiungi un parametro a una funzione, il compilatore ti elenca ogni chiamata da aggiornare, così non te ne sfugge nessuna. (Cose come println!, che accettano davvero un numero qualsiasi di valori, sono macro: è questo che significa il !. Vedi Macro.)

Come funzionano i parametri lo trovi spiegato in Funzioni.

Soluzione 1: Passa esattamente ciò che chiede la firma

Sezione intitolata “Soluzione 1: Passa esattamente ciò che chiede la firma”

Sceglila quasi sempre. Vai al punto indicato dalla note:, leggi l’elenco dei parametri e passa un valore per ogni parametro, nell’ordine giusto:

fn greet(name: &str, greeting: &str) {
println!("{greeting}, {name}!");
}
fn main() {
greet("Ferris", "Ciao");
greet("Corro", "Buongiorno");
}

Output:

Ciao, Ferris!
Buongiorno, Corro!
▶ Provalo tu

Se hai appena cambiato la funzione e ora tutte le chiamate sono rotte, è il compilatore che ti sta aiutando: correggi ogni chiamata che elenca, oppure chiediti se il nuovo parametro non dovrebbe essere facoltativo (Soluzione 3).

Scegli questa quando la funzione ha un parametro self. Di solito un metodo si chiama come valore.metodo(argomenti). Il valore prima del punto riempie self, quindi non lo conti. Se invece scrivi Type::method(argomenti), niente riempie 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);
| +++++++++++++++++++

Fai caso al conteggio. Chiamata come Account::deposit(...), rustc dice che la funzione prende 2 argomenti, perché &mut self conta. Chiamata come acc.deposit(), dice che il metodo prende 1 argomento, perché il punto fornisce già self. Entrambe le forme funzionano quando ci sono tutti gli argomenti:

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); // il modo abituale
Account::deposit(&mut acc, 25); // la stessa chiamata, scritta per esteso
println!("Saldo: {}", acc.balance);
}

Output:

Saldo: 75
▶ Provalo tu

Nel codice di tutti i giorni usa la forma con il punto. Type::method(&x, a) è utile quando devi passare il metodo stesso da qualche parte, come team.iter().map(Player::label) (esempio). Se una funzione non ha proprio self (come new), si può chiamare solo come Type::function(...). Vedi Struct ed E0424.

Soluzione 3: Usa Option<T> per un argomento che a volte non serve

Sezione intitolata “Soluzione 3: Usa Option<T> per un argomento che a volte non serve”

Scegli questa quando un argomento di solito ha un valore predefinito sensato. Rendilo un Option, passa None per dire «usa il predefinito», e completalo con unwrap_or:

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

Output:

Connessione a localhost:8080 (timeout 30s)
Connessione a example.com:443 (timeout 5s)
▶ Provalo tu

A essere onesti: chi chiama scrive comunque l’argomento, None. Rust lo preferisce, perché la chiamata mostra che si sta usando un valore predefinito. Se non vuoi scriverlo per niente, crea invece due funzioni con nomi chiari, come connect(host, port) che chiama connect_with_timeout(host, port, 30). È la risposta tipica di Rust all’overloading. Vedi Option.

Soluzione 4: Usa una struct di configurazione con Default

Sezione intitolata “Soluzione 4: Usa una struct di configurazione con Default”

Scegli questa quando una funzione ha molte impostazioni e la maggior parte di chi la chiama ne cambia solo una o due. Metti le impostazioni in una struct, implementa Default e lascia che chi chiama compili solo ciò che gli serve con ..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!(
"In ascolto su {}:{} (timeout {}s, verbose: {})",
config.host, config.port, config.timeout_secs, config.verbose
);
}
fn main() {
start(ServerConfig::default());
start(ServerConfig { port: 3000, verbose: true, ..Default::default() });
}

Output:

In ascolto su localhost:8080 (timeout 30s, verbose: false)
In ascolto su localhost:3000 (timeout 30s, verbose: true)
▶ Provalo tu

In più ottieni qualcosa di simile agli argomenti con nome: port: 3000 dice cosa significa il numero. Se il valore predefinito di ogni campo è quello del suo tipo (0, false, stringa vuota), #[derive(Default)] scrive l’impl al posto tuo. Per API più grandi, il passo successivo è il builder pattern (Server::builder().port(3000).build()). Vedi Struct e Design pattern.

Dopo aver aggiunto un parametro. Dai a fn save(path: &str) un secondo parametro, e ogni vecchia chiamata save("notes.txt") si rompe con E0061. Soluzione: aggiorna ogni chiamata elencata dal compilatore, oppure usa la Soluzione 3 o 4 se il nuovo valore di solito è sempre lo stesso.

I metodi della libreria standard. names.push("Cy", "Di") dà «this method takes 1 argument but 2 arguments were supplied». push aggiunge un solo elemento. Soluzione: chiamalo due volte, oppure usa names.extend(["Cy", "Di"]). Vedi Vettori.

Le tuple struct. struct Rgb(u8, u8, u8); e poi Rgb(255, 0) dà «this struct takes 3 arguments but 2 arguments were supplied». Soluzione: dai un valore a ogni campo, Rgb(255, 0, 0).

Abitudini da altri linguaggi. Il greet("Ferris", greeting="Hi") di Python non è sintassi Rust. Scrivere greet(greeting = "Hi") non dà un nome a un argomento; rustc lo legge come un’assegnazione a una variabile chiamata greeting e segnala anche E0425. Soluzione: passa i valori per posizione, oppure usa una struct di configurazione (Soluzione 4).

Le closure. Chiamare add(1) quando add è |a: i32, b: i32| a + b dà lo stesso messaggio, ma con il codice E0057 (la versione per le closure). La soluzione è la stessa: passa entrambi i valori.

  • E0308: il numero di argomenti è giusto, ma uno ha il tipo sbagliato, spesso perché due sono stati scambiati.
  • E0424: self usato in una funzione senza parametro self, l’altra faccia di «metodo o funzione associata».
  • E0599: non esiste nessun metodo con quel nome, per esempio quando chiami con il punto una funzione associata come new.
  • E0425: non si trova un valore; può comparire accanto a E0061 quando provi gli argomenti con nome.
  • Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori. Collegati anche: E0057 (closure chiamata con il numero sbagliato di argomenti) ed E0428 (una funzione definita due volte, quando provi l’overloading), che non ha una pagina tutta sua.

Verifica veloce

1. fn area(w: u32, h: u32) -> u32 viene chiamata come area(5). Cosa segnala rustc?

2. Un metodo è fn deposit(&mut self, amount: i64). Quale chiamata è corretta?

3. Vuoi un argomento timeout che alla maggior parte di chi chiama non interessa. Qual è un approccio comune in Rust?

Punteggio: 0 / 3