Rediriger les erreurs vers la sortie d’erreur standard
Pour le moment, nous écrivons toutes nos sorties sur le terminal en utilisant la macro println!. Dans la plupart des terminaux, il existe deux types de sortie : la sortie standard (stdout) pour les informations générales et la sortie d’erreur standard (stderr) pour les messages d’erreur. Cette distinction permet aux utilisateurs de choisir de rediriger la sortie réussie d’un programme vers un fichier tout en affichant les messages d’erreur à l’écran.
La macro println! n’est capable d’écrire que sur la sortie standard, nous devons donc utiliser autre chose pour écrire sur la sortie d’erreur standard.
Vérifier où les erreurs sont écrites
Tout d’abord, observons comment le contenu affiché par minigrep est actuellement écrit sur la sortie standard, y compris les messages d’erreur que nous voudrions écrire sur la sortie d’erreur standard à la place. Nous ferons cela en redirigeant le flux de sortie standard vers un fichier tout en provoquant intentionnellement une erreur. Nous ne redirigerons pas le flux d’erreur standard, donc tout contenu envoyé vers la sortie d’erreur standard continuera à s’afficher à l’écran.
Les programmes en ligne de commande sont censés envoyer les messages d’erreur sur le flux d’erreur standard afin que nous puissions toujours voir les messages d’erreur à l’écran même si nous redirigeons le flux de sortie standard vers un fichier. Notre programme ne se comporte pas correctement actuellement : nous allons voir qu’il enregistré le message d’erreur dans un fichier à la place !
Pour démontrer ce comportement, nous exécuterons le programme avec > et le chemin de fichier, output.txt, vers lequel nous voulons rediriger le flux de sortie standard. Nous ne passerons aucun argument, ce qui devrait provoquer une erreur :
$ cargo run > output.txt
La syntaxe > indique au shell d’écrire le contenu de la sortie standard dans output.txt au lieu de l’écran. Nous n’avons pas vu le message d’erreur que nous attendions à l’écran, ce qui signifie qu’il a dû finir dans le fichier. Voici ce que contient output.txt :
Problem parsing arguments: not enough arguments
En effet, notre message d’erreur est affiché sur la sortie standard. Il est beaucoup plus utile que les messages d’erreur comme celui-ci soient affichés sur la sortie d’erreur standard afin que seules les données d’une exécution réussie se retrouvent dans le fichier. Nous allons changer cela.
Afficher les erreurs sur la sortie d’erreur standard
Nous utiliserons le code de l’encart 12-24 pour changer la façon dont les messages d’erreur sont affichés. Grâce au refactoring que nous avons fait plus tôt dans ce chapitre, tout le code qui affiche des messages d’erreur se trouve dans une seule fonction, main. La bibliothèque standard fournit la macro eprintln! qui écrit sur le flux d’erreur standard, donc changeons les deux endroits où nous appelions println! pour afficher les erreurs afin d’utiliser eprintln! à la place.
use std::env;
use std::error::Error;
use std::fs;
use std::process;
use minigrep::{search, search_case_insensitive};
fn main() {
let args: Vec<String> = env::args().collect();
let config = Config::build(&args).unwrap_or_else(|err| {
eprintln!("Problem parsing arguments: {err}");
process::exit(1);
});
if let Err(e) = run(config) {
eprintln!("Application error: {e}");
process::exit(1);
}
}
pub struct Config {
pub query: String,
pub file_path: String,
pub ignore_case: bool,
}
impl Config {
fn build(args: &[String]) -> Result<Config, &'static str> {
if args.len() < 3 {
return Err("not enough arguments");
}
let query = args[1].clone();
let file_path = args[2].clone();
let ignore_case = env::var("IGNORE_CASE").is_ok();
Ok(Config {
query,
file_path,
ignore_case,
})
}
}
fn run(config: Config) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(config.file_path)?;
let results = if config.ignore_case {
search_case_insensitive(&config.query, &contents)
} else {
search(&config.query, &contents)
};
for line in results {
println!("{line}");
}
Ok(())
}
eprintln!Exécutons maintenant à nouveau le programme de la même manière, sans aucun argument et en redirigeant la sortie standard avec > :
$ cargo run > output.txt
Problem parsing arguments: not enough arguments
Maintenant, nous voyons l’erreur à l’écran et output.txt ne contient rien, ce qui est le comportement attendu des programmes en ligne de commande.
Exécutons à nouveau le programme avec des arguments qui ne provoquent pas d’erreur mais en redirigeant toujours la sortie standard vers un fichier, comme ceci :
$ cargo run -- to poem.txt > output.txt
Nous ne verrons aucune sortie sur le terminal, et output.txt contiendra nos résultats :
Fichier : output.txt
Are you nobody, too?
How dreary to be somebody!
Cela démontre que nous utilisons maintenant la sortie standard pour les sorties réussies et la sortie d’erreur standard pour les sorties d’erreur, comme il se doit.
Résumé
Ce chapitre a récapitulé certains des concepts majeurs que vous avez appris jusqu’ici et a couvert la façon d’effectuer des opérations d’E/S courantes en Rust. En utilisant les arguments de ligne de commande, les fichiers, les variables d’environnement et la macro eprintln! pour afficher les erreurs, vous êtes maintenant prêt à écrire des applications en ligne de commande. Combiné avec les concepts des chapitres précédents, votre code sera bien organisé, stockera les données efficacement dans les structures de données appropriées, gérera les erreurs correctement et sera bien testé.
Ensuite, nous explorerons certaines fonctionnalités de Rust qui ont été influencées par les langages fonctionnels : les fermetures (closures) et les itérateurs.