Ecrire les messages d'erreur sur la sortie d'erreur standard au lieu de la sortie normale

Pour l'instant, nous avons écrit toutes nos sorties du terminal en utilisant la macro println!. Dans la plupart des terminaux, il y a deux genres de sorties : 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 à l'utilisateur de choisir de rediriger la sortie des messages sans erreurs d'un programme vers un fichier mais continuer à afficher les messages d'erreur à l'écran.

La macro println! ne peut écrire que sur la sortie standard, donc nous devons utiliser autre chose pour écrire sur la sortie d'erreur standard.

Vérifier où sont écrites les erreurs

Commençons par observer comment le contenu écrit par minigrep est actuellement écrit sur la sortie standard, y compris les messages d'erreur que nous souhaitons plutôt écrire sur la sortie d'erreur standard. Nous allons faire cela en redirigeant le flux de sortie standard vers un fichier pendant que nous déclencherons intentionnellement une erreur. Nous ne redirigerons pas le flux de sortie d'erreur standard, si bien que n'importe quel contenu envoyé à la sortie d'erreur standard va continuer à s'afficher à l'écran.

Les programmes en ligne de commande sont censés envoyer leurs messages d'erreur dans le flux d'erreurs standard afin que nous puissions continuer à voir les messages d'erreurs à l'écran même si nous redirigeons le flux de la sortie standard dans un fichier. Notre programme ne se comporte pas comme il le devrait : nous allons voir qu'à la place, il envoie les messages d'erreur dans le fichier !

Pour démontrer ce comportement, il faut exécuter le programme avec > suivi du nom du fichier, sortie.txt, dans lequel nous souhaitons rediriger le flux de sortie standard. Nous ne fournissons aucun argument, ce qui va causer une erreur :

$ cargo run > sortie.txt

La syntaxe indique à l'invite de commande d'écrire le contenu de la sortie standard dans sortie.txt plutôt qu'à l'écran. Nous n'avons pas vu le message d'erreur que nous nous attendions de voir à l'écran, ce qui veut dire qu'il a dû finir dans le fichier. Voici ce que sortie.txt contient :

Problème rencontré lors de l'interprétation des arguments : il n'y a pas assez d'arguments

Effectivement, notre message d'erreur est écrit sur la sortie standard. Il serait bien plus utile que les messages d'erreur comme celui-ci soient écrits sur la sortie d'erreur standard afin que seules les données produites par exécution fructueuse finissent dans le fichier. Nous allons corriger cela.

Ecrire les erreurs sur la sortie d'erreur standard

Nous allons utiliser le code de l'encart 12-24 pour changer la manière dont les messages d'erreur sont écrits. Grâce au remaniement que nous avons fait plus tôt dans ce chapitre, tout le code qui écrit les messages d'erreurs se trouve dans une seule fonction, main. La bibliothèque standard fournit la macro eprintln! qui écrit dans le flux d'erreur standard, donc changeons les deux endroits où nous appelons println! afin d'utiliser eprintln! à la place.

Fichier : src/main.rs

use std::env;
use std::process;

use minigrep::Config;

fn main() {
    let args: Vec<String> = env::args().collect();

    let config = Config::new(&args).unwrap_or_else(|err| {
        eprintln!("Problème rencontré lors de l'interprétation des arguments : {}", err);
        process::exit(1);
    });

    if let Err(e) = minigrep::run(config) {
        eprintln!("Erreur applicative : {}", e);

        process::exit(1);
    }
}

Encart 12-24 : Ecrire les messages d'erreur sur la sortie d'erreur standard au lieu de la sortie standard en utilisant eprintln!

Après avoir changé println! en eprintln!, exécutons à nouveau le programme de la même manière, sans aucun argument et en redirigeant la sortie standard avec > :

$ cargo run > sortie.txt
Problème rencontré lors de l'interprétation des arguments : il n'y a pas assez d'arguments

Désormais nous pouvons voir l'erreur à l'écran et sortie.txt ne contient rien, ce qui est le comportement que nous attendons d'un programme en ligne de commande.

Exécutons le programme à nouveau avec des arguments qui ne causent pas d'erreur tout en continuant à rediriger la sortie standard vers un fichier, comme ceci :

$ cargo run to poem.txt > sortie.txt

Nous ne voyons rien sur la sortie du terminal, et sortie.txt devrait contenir notre résultat :

Fichier : sortie.txt

Are you nobody, too?
How dreary to be somebody!

Ceci prouve qu'en fonction des circonstances, nous utilisons maintenant la sortie standard pour la sortie sans les erreurs et l'erreur standard pour la sortie d'erreur.

Résumé

Ce chapitre a résumé certains des concepts majeurs que vous avez appris précédemment et expliqué comment procéder à des opérations courantes sur les entrées/sorties en Rust. En utilisant les arguments en ligne de commande, les fichiers, les variables d'environnement et la macro eprintln! pour écrire les erreurs, vous pouvez désormais écrire des applications en ligne de commande. En suivant les concepts vus dans les chapitres précédents, votre code restera bien organisé, stockera les données dans les bonnes structures de données, gérera correctement les erreurs et sera correctement testé.

Maintenant, nous allons découvrir quelques fonctionnalités de Rust qui ont été influencées par les langages fonctionnels : les fermetures et les itérateurs.