Les arguments de ligne de commande en Rust
La plupart des petits projets qui valent la peine d’être construits pour s’entraîner (une liste de tâches, un compteur de mots…) ont besoin de recevoir des données depuis la ligne de commande, plutôt que de les écrire en dur dans le code source. Cette page couvre les bases.
Lire les arguments avec std::env::args
Section intitulée « Lire les arguments avec std::env::args »use std::env;
fn main() { let args: Vec<String> = env::args().collect(); println!("{:?}", args);}Si vous compiliez ce code en un programme appelé greet et que vous lanciez :
./greet AliceVous verriez :
["greet", "Alice"]Le premier élément est toujours le nom du programme lui-même : les arguments que vous avez réellement tapés commencent à l’indice 1.
Lire un argument précis
Section intitulée « Lire un argument précis »use std::env;
fn main() { let args: Vec<String> = env::args().collect();
if args.len() < 2 { println!("Utilisation : greet <nom>"); return; }
let name = &args[1]; println!("Bonjour, {name} !");}Lancez-le avec cargo run -- Alice et vous obtiendrez :
Bonjour, Alice !Lancez-le sans argument et vous verrez le message d’utilisation au lieu d’un plantage : vérifier args.len() avant d’accéder à un indice évite une panic si quelqu’un oublie de passer un argument.
Convertir un argument en nombre
Section intitulée « Convertir un argument en nombre »Comme chaque argument arrive sous forme de String, vous aurez souvent besoin de .parse() (vu sur la page Conversion de types) pour le transformer en nombre :
use std::env;
fn main() { let args: Vec<String> = env::args().collect();
if args.len() < 2 { println!("Utilisation : square <nombre>"); return; }
match args[1].parse::<i32>() { Ok(n) => println!("{}", n * n), Err(_) => println!("Ce n'est pas un nombre valide"), }}Gérer un flag simple à la main
Section intitulée « Gérer un flag simple à la main »Un flag (option) est un argument qui active quelque chose, comme --shout. Avant de recourir à une crate, il est utile de voir comment le gérer soi-même. L’astuce qui rend ce code facile à tester : placer la logique dans une fonction qui reçoit les arguments sous forme de slice, &[String]. Ainsi, main peut lui passer les vrais arguments, et vous pouvez lui passer une liste faite à la main pendant vos essais :
fn run(args: &[String]) { let mut shout = false; let mut names = Vec::new();
// on saute l'indice 0, le nom du programme for arg in &args[1..] { if arg == "--shout" { shout = true; } else { names.push(arg); } }
if names.is_empty() { println!("Utilisation : greet [--shout] <nom>..."); return; }
for name in names { let line = format!("Bonjour, {name} !"); if shout { println!("{}", line.to_uppercase()); } else { println!("{line}"); } }}
fn main() { // Dans un vrai programme, vous écririez : // let args: Vec<String> = std::env::args().collect(); // Ici, on construit la liste à la main pour que ça marche aussi dans le Playground. let args = vec![ String::from("greet"), String::from("--shout"), String::from("Ana"), String::from("Ben"), ]; run(&args);}Sortie :
BONJOUR, ANA !BONJOUR, BEN !Quelques points à remarquer :
&args[1..]contient tous les arguments sauf le nom du programme. Le flag peut apparaître n’importe où :greet Ana --shout Benfonctionne aussi.- Retirez
--shoutde la liste et relancez : vous obtenez les salutations normales. Retirez aussi les noms, et c’est le message d’utilisation qui s’affiche. - Comme
runne se soucie pas de l’origine de sa liste, passer àstd::env::args().collect()ne demande de changer qu’une ligne dansmain.
Cette approche suffit pour un ou deux flags. Dès que vous voulez aussi des versions courtes (-s), des valeurs après un flag (--count 3) et un message --help, la boucle écrite à la main grossit très vite. C’est le moment de passer à clap.
Au-delà de quelques arguments : utilisez clap
Section intitulée « Au-delà de quelques arguments : utilisez clap »Accéder à la main aux indices de args devient vite ingérable dès qu’il vous faut des flags (--verbose), des valeurs optionnelles ou une aide --help utile. La solution standard est la crate clap, que l’on ajoute avec :
cargo add clap --features deriveuse clap::Parser;
#[derive(Parser)]struct Args { name: String,
#[arg(short, long, default_value_t = 1)] count: u32,}
fn main() { let args = Args::parse(); for _ in 0..args.count { println!("Bonjour, {} !", args.name); }}Vous obtenez ainsi l’analyse des arguments, leur validation et un message --help fonctionnel, le tout généré à partir de la définition de la struct. Vous n’avez pas besoin de clap pour des petits scripts rapides, mais pour tout outil que vous utiliserez plus d’une fois, il vous épargne beaucoup de travail manuel.
Exercices
Section intitulée « Exercices »Le Playground ne peut pas passer de vrais arguments, donc les deux exercices partent d’un Vec<String> fait à la main, identique à ce que vous donnerait env::args().collect(). Sur votre machine, remplacez-le par le vrai appel et essayez cargo run -- ....
1. Lister les arguments
Section intitulée « 1. Lister les arguments »À partir de vec!["notes", "add", "acheter du lait"] (sous forme de String), affichez combien d’arguments l’utilisateur a tapés (sans compter le nom du programme), puis affichez chacun avec sa position, en commençant à 1.
Indice : le nombre d’arguments est args.len() - 1. Une boucle for i in 1..args.len() vous donne chaque indice après le nom du programme.
Voir la solution
fn main() { // imaginons que ceci vienne de std::env::args().collect() let args = vec![ String::from("notes"), String::from("add"), String::from("acheter du lait"), ];
println!("Vous avez passé {} arguments", args.len() - 1); for i in 1..args.len() { println!("{i} : {}", args[i]); }}Sortie :
Vous avez passé 2 arguments1 : add2 : acheter du lait"acheter du lait" compte comme un seul argument : sur une vraie ligne de commande, vous le taperiez entre guillemets (cargo run -- add "acheter du lait"), et le shell le transmet comme une seule chaîne. L’indice 0 reste le nom du programme, c’est pour ça que la boucle commence à 1.
2. Additionner les nombres
Section intitulée « 2. Additionner les nombres »À partir de vec!["sum", "4", "10", "x", "6"], additionnez tous les arguments qui sont des nombres valides et affichez un avertissement pour ceux qui n’en sont pas. Terminez en affichant le total.
Indice : parcourez &args[1..] et utilisez match arg.parse::<i32>(), comme dans Convertir un argument en nombre. Déclarez let mut total = 0; en dehors de la boucle.
Voir la solution
fn main() { let args = vec![ String::from("sum"), String::from("4"), String::from("10"), String::from("x"), String::from("6"), ];
let mut total = 0; for arg in &args[1..] { match arg.parse::<i32>() { Ok(n) => total += n, Err(_) => println!("On ignore '{arg}' : ce n'est pas un nombre"), } } println!("Total : {total}");}Sortie :
On ignore 'x' : ce n'est pas un nombreTotal : 20Chaque argument arrive sous forme de texte, même "4", donc chacun doit être converti. Ignorer une entrée invalide avec un message est plus agréable qu’un plantage. Vous pourriez aussi vous arrêter à la première erreur avec return, si cela convient mieux à votre programme.
Récapitulatif
Section intitulée « Récapitulatif »std::env::args().collect()vous donne unVec<String>des arguments de ligne de commande ; l’indice 0 est toujours le nom du programme.- Vérifiez
args.len()avant d’accéder à un indice, pour qu’un argument manquant ne fasse pas paniquer votre programme. .parse()transforme un argumentStringen nombre, comme partout ailleurs.- Placez la gestion des arguments dans une fonction qui reçoit
&[String], pour pouvoir la tester avec une liste faite à la main. - Au-delà de quelques arguments simples, la crate
clapest le choix standard et éprouvé.
Petit quiz
Section intitulée « Petit quiz »Petit quiz
1. Quand vous appelez env::args().collect(), quel est le premier élément du Vec obtenu ?
2. Pourquoi vérifier args.len() avant d'accéder à un indice de args ?
3. Qu'apporte la crate clap par rapport à l'accès manuel aux indices de args ?
Score : 0 / 3
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Accepting Command Line Arguments — The Rust Book (en anglais) : le début du projet
minigrepdu Book, qui lit un mot à chercher et un nom de fichier depuis la ligne de commande. std::env::args— documentation de la bibliothèque standard (en anglais) : ce que renvoie exactementargs(), et quand il panique (arguments qui ne sont pas de l’Unicode valide).- Program arguments — Rust by Example (en anglais) : un court exemple, suivi d’une page sur le filtrage des arguments avec
match. - Command Line Applications in Rust (en anglais) : un mini-livre gratuit sur la création de vrais outils en ligne de commande, de l’analyse des arguments jusqu’aux tests et à la distribution.
- clap derive tutorial — docs.rs (en anglais) : un guide pas à pas sur les flags, les options, les sous-commandes et la validation avec
clap.