Erreur Rust E0061 : this function takes N arguments but M were supplied
E0061 veut dire que vous avez appelé une fonction avec le mauvais nombre d’arguments : trop peu ou trop. C’est l’une des erreurs les plus sympathiques de Rust, parce que rustc vous dit exactement quel argument manque ou est en trop, et de quel type il devrait être. On la voit souvent chez les personnes qui viennent de Python, JavaScript ou C#, où les arguments par défaut et la surcharge sont monnaie courante. Rust n’a ni l’un ni l’autre, et cette page vous montre quoi faire à la place. Pour les autres erreurs de cette série, voir Les erreurs du compilateur Rust.
Le message d’erreur
Section intitulée « Le message d’erreur »error[E0061]: this function takes 2 arguments but 1 argument was supplied --> src/main.rs:6:5 |6 | greet("Ferris"); // ❌ greet veut deux 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 veut deux arguments | ++++++++++++En français : « cette fonction prend 2 arguments, mais 1 seul a été fourni ». L’étiquette précise que l’argument n° 2, de type &str, manque.
Voici le programme qui la provoque :
fn greet(name: &str, greeting: &str) { println!("{greeting}, {name}!");}
fn main() { greet("Ferris"); // ❌ greet veut deux arguments}Trop d’arguments donne le même code, avec une étiquette « unexpected argument » (argument inattendu) et une aide « remove the extra argument » (retirez l’argument en trop) :
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}`Pour une méthode appelée avec un point, la première ligne dit « this method takes… » à la place. Pour une tuple struct comme Rgb(255, 0), elle dit « this struct takes 3 arguments ». C’est à chaque fois la même erreur.
Ce que ça veut dire
Section intitulée « Ce que ça veut dire »Le message a trois parties, et chacune vous aide :
- La première ligne compte : la fonction veut 2 arguments, vous en avez donné 1.
- L’étiquette sous votre appel nomme le problème : « argument #2 of type
&stris missing » (l’argument n° 2, de type&str, manque). - La
note:renvoie à la définition de la fonction et souligne le paramètre que vous avez oublié,greeting: &str.
La ligne help: montre où va l’argument. /* &str */ n’est qu’un repère qui vous indique le type ; c’est un commentaire, donc le coller tel quel ne compilera pas. Mettez une vraie valeur à sa place, comme "Bonjour".
C’est comme une machine à café qui demande à la fois une tasse et une pièce. Mettre seulement la pièce ne vous donne pas un demi-café. La machine ne démarre pas, c’est tout.
Pourquoi Rust fait ça
Section intitulée « Pourquoi Rust fait ça »En Rust, la signature d’une fonction est un contrat précis : ces paramètres, dans cet ordre, avec ces types. Chaque appel doit le respecter. Il n’y a pas d’arguments par défaut (greeting = "Hello"), pas de surcharge (deux fonctions nommées greet avec des paramètres différents), ni de fonctions qui acceptent « n’importe quel nombre » d’arguments. Essayer d’écrire une valeur par défaut donne error: parameter defaults are not supported, et une deuxième fonction greet donne E0428, « the name greet is defined multiple times » (le nom greet est défini plusieurs fois).
Ça a l’air strict, mais c’est payant. Quand vous lisez greet(name, greeting), vous savez exactement quelle fonction s’exécute et ce qu’elle reçoit. Quand vous ajoutez un paramètre à une fonction, le compilateur liste tous les appels à mettre à jour : aucun n’est oublié. (Les choses comme println!, qui acceptent vraiment n’importe quel nombre de valeurs, sont des macros : c’est ce que veut dire le !. Voir Les macros.)
Le fonctionnement des paramètres est expliqué dans Les fonctions.
Comment corriger
Section intitulée « Comment corriger »Solution 1 : passer exactement ce que demande la signature
Section intitulée « Solution 1 : passer exactement ce que demande la signature »Choisissez cette solution presque à chaque fois. Ouvrez l’emplacement indiqué par la note:, lisez la liste des paramètres, et passez une valeur par paramètre, dans l’ordre :
fn greet(name: &str, greeting: &str) { println!("{greeting}, {name} !");}
fn main() { greet("Ferris", "Bonjour"); greet("Corro", "Bonne journée");}Sortie :
Bonjour, Ferris !Bonne journée, Corro !Si vous venez de modifier la fonction et que tous les appels sont maintenant cassés, c’est le compilateur qui vous aide : corrigez chaque appel qu’il liste, ou demandez-vous si le nouveau paramètre devrait vraiment être facultatif (solution 3).
Solution 2 : appeler les méthodes avec un point
Section intitulée « Solution 2 : appeler les méthodes avec un point »Choisissez cette solution quand la fonction a un paramètre self. Une méthode s’appelle normalement sous la forme valeur.methode(args). La valeur avant le point remplit self, vous ne la comptez donc pas. Si vous écrivez Type::methode(args) à la place, rien ne remplit 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); | +++++++++++++++++++Ici, c’est l’argument n° 1, &mut Account, qui manque : c’est la place de self.
Remarquez le décompte. Appelée sous la forme Account::deposit(...), rustc dit que la fonction prend 2 arguments, parce que &mut self compte. Appelée sous la forme acc.deposit(), il dit que la méthode prend 1 argument, parce que le point fournit déjà self. Les deux formes fonctionnent dès que tous les arguments sont 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); // la façon habituelle Account::deposit(&mut acc, 25); // le même appel, écrit en entier println!("Solde : {}", acc.balance);}Sortie :
Solde : 75Utilisez la forme avec le point dans le code de tous les jours. Type::methode(&x, a) est utile quand vous devez passer la méthode elle-même quelque part, comme team.iter().map(Player::label) (exemple). Si une fonction n’a pas du tout de self (comme new), elle ne peut s’appeler que sous la forme Type::fonction(...). Voir Les structs et E0424.
Solution 3 : utiliser Option<T> pour un argument qui n’est pas toujours nécessaire
Section intitulée « Solution 3 : utiliser Option<T> pour un argument qui n’est pas toujours nécessaire »Choisissez cette solution quand un argument a le plus souvent une valeur par défaut raisonnable. Faites-en une Option, passez None pour « utiliser la valeur par défaut », et complétez-la avec unwrap_or :
fn connect(host: &str, port: u16, timeout_secs: Option<u64>) { let timeout = timeout_secs.unwrap_or(30); println!("Connexion à {host}:{port} (délai {timeout} s)");}
fn main() { connect("localhost", 8080, None); connect("example.com", 443, Some(5));}Sortie :
Connexion à localhost:8080 (délai 30 s)Connexion à example.com:443 (délai 5 s)Soyons honnêtes : les appelants écrivent quand même l’argument, None. Rust préfère ça, parce que l’appel montre qu’une valeur par défaut est utilisée. Si vous préférez ne pas l’écrire du tout, créez plutôt deux fonctions aux noms clairs, comme connect(host, port) qui appelle connect_with_timeout(host, port, 30). C’est la réponse habituelle de Rust à la surcharge. Voir Option.
Solution 4 : utiliser une struct de configuration avec Default
Section intitulée « Solution 4 : utiliser une struct de configuration avec Default »Choisissez cette solution quand une fonction a beaucoup de réglages et que la plupart des appelants n’en changent qu’un ou deux. Rangez les réglages dans une struct, implémentez Default, et laissez les appelants ne remplir que ce dont ils ont besoin avec ..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!( "En écoute sur {}:{} (délai {} s, verbose : {})", config.host, config.port, config.timeout_secs, config.verbose );}
fn main() { start(ServerConfig::default()); start(ServerConfig { port: 3000, verbose: true, ..Default::default() });}Sortie :
En écoute sur localhost:8080 (délai 30 s, verbose : false)En écoute sur localhost:3000 (délai 30 s, verbose : true)Vous obtenez aussi une sorte d’arguments nommés : port: 3000 dit ce que signifie le nombre. Si la valeur par défaut de chaque champ est celle de son type (0, false, chaîne vide), #[derive(Default)] écrit l’impl pour vous. Pour des API plus grandes, le builder pattern (patron « monteur »), par exemple Server::builder().port(3000).build(), est l’étape suivante. Voir Les structs et Les design patterns.
Où vous le verrez souvent
Section intitulée « Où vous le verrez souvent »Après avoir ajouté un paramètre. Vous donnez un deuxième paramètre à fn save(path: &str), et tous les anciens appels save("notes.txt") cassent avec E0061. Correction : mettez à jour chaque appel que le compilateur liste, ou utilisez la solution 3 ou 4 si la nouvelle valeur est presque toujours la même.
Les méthodes de la bibliothèque standard. names.push("Cy", "Di") donne « this method takes 1 argument but 2 arguments were supplied ». push ajoute un seul élément. Correction : appelez-la deux fois, ou utilisez names.extend(["Cy", "Di"]). Voir Les vecteurs.
Les tuple structs. struct Rgb(u8, u8, u8); puis Rgb(255, 0) donne « this struct takes 3 arguments but 2 arguments were supplied ». Correction : donnez tous les champs, Rgb(255, 0, 0).
Les habitudes venues d’autres langages. Le greet("Ferris", greeting="Hi") de Python n’est pas de la syntaxe Rust. Écrire greet(greeting = "Hi") ne nomme pas un argument ; rustc le lit comme une affectation à une variable appelée greeting, et signale en plus E0425. Correction : passez les valeurs par position, ou utilisez une struct de configuration (solution 4).
Les closures. Appeler add(1) alors que add vaut |a: i32, b: i32| a + b donne le même message, mais sous le code E0057 (la version closure). La correction est la même : passez les deux valeurs.
Erreurs liées
Section intitulée « Erreurs liées »- E0308 : le nombre d’arguments est bon, mais l’un d’eux a le mauvais type, souvent parce que deux arguments ont été inversés.
- E0424 :
selfutilisé dans une fonction sans paramètreself, l’autre face de la différence entre méthode et fonction associée. - E0599 : aucune méthode de ce nom n’existe, par exemple quand on appelle une fonction associée comme
newavec un point. - E0425 : impossible de trouver une valeur ; peut apparaître à côté de E0061 quand vous essayez les arguments nommés.
- Toutes les erreurs du compilateur : la liste complète des pages d’erreurs. Liées aussi : E0057 (closure appelée avec le mauvais nombre d’arguments) et E0428 (une fonction définie deux fois, en essayant de la surcharger), qui n’a pas de page à elle.
Petit quiz
Section intitulée « Petit quiz »Petit quiz
1. fn area(w: u32, h: u32) -> u32 est appelée avec area(5). Que signale rustc ?
2. Une méthode est déclarée fn deposit(&mut self, amount: i64). Quel appel est correct ?
3. Vous voulez un argument timeout dont la plupart des appelants se moquent. Quelle est une approche courante en Rust ?
Score : 0 / 3
Pour aller plus loin
Section intitulée « Pour aller plus loin »- E0061 — Rust error codes index (en anglais) : l’explication officielle de cette erreur.
- Functions — The Rust Book (en anglais) : les paramètres, les types dans les signatures et le fonctionnement des appels.
- Method Syntax — The Rust Book (en anglais) : pourquoi
selfest rempli par le point, et les fonctions associées commenew. - Le trait
Default— documentation de la bibliothèque standard (en anglais) : dériverDefaultet utiliser..Default::default(). - Builder pattern — Rust Design Patterns (en anglais) : le remplaçant habituel des longues listes d’arguments et des arguments facultatifs.