Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Scrivere i Messaggi di Errore su Standard Error

Al momento, stiamo stampando tutto il nostro output sul terminale usando la macro println!. Nella maggior parte dei terminali, esistono due tipi di output: standard output (stdout) per informazioni generali e standard error (stderr) per i messaggi di errore. Questa distinzione consente agli utenti di scegliere di indirizzare l’output corretto di un programma a un file, ma di visualizzare comunque i messaggi di errore sullo schermo.

La macro println! è in grado di stampare solo sullo standard output, quindi dobbiamo usare qualcos’altro per stampare sullo standard error.

Controllare dove Vengono Scritti gli Errori

Per prima cosa osserviamo come il contenuto stampato da minigrep viene attualmente scritto sullo standard output, inclusi eventuali messaggi di errore che vorremmo invece scrivere sullo standard error. Lo faremo reindirizzando il flusso di standard output a un file, causando intenzionalmente un errore. Non reindirizzeremo il flusso di standard error, quindi qualsiasi contenuto inviato allo standard error continuerà a essere visualizzato sullo schermo.

Ci si aspetta che i programmi a riga di comando inviino messaggi di errore al flusso di standard error, in modo da poterli comunque visualizzare sullo schermo anche se reindirizziamo il flusso di output standard a un file. Il nostro programma al momento non si comporta bene: stiamo per vedere che salverà l’output del messaggio di errore in un file!

Per dimostrare questo comportamento, esegui il programma con > e il percorso del file, output.txt, a cui vogliamo reindirizzare il flusso di standard output. Non passeremo alcun argomento, il che dovrebbe causare un errore:

$ cargo run > output.txt

La sintassi > indica alla shell di scrivere il contenuto dello standard output su output.txt invece che sullo schermo. Non abbiamo visto il messaggio di errore che ci aspettavamo di visualizzare sullo schermo, quindi significa che deve essere finito nel file. Ecco cosa contiene output.txt:

Problema nella lettura degli argomenti: non ci sono abbastanza argomenti

Sì, il nostro messaggio di errore viene visualizzato sullo standard output. È molto più utile che messaggi di errore come questo vengano visualizzati sullo standard error, in modo che solo i dati di un’esecuzione senza errori finiscano nel file. Cambieremo questa impostazione.

Visualizzazione degli Errori sullo Standard Error

Useremo il codice del Listato 12-24 per modificare la modalità di visualizzazione dei messaggi di errore. A causa del refactoring effettuato in precedenza in questo capitolo, tutto il codice che visualizza i messaggi di errore si trova in un’unica funzione, main. La libreria standard fornisce la macro eprintln! che stampa sullo standard error, quindi modifichiamo i due punti in cui chiamavamo println! per visualizzare gli errori in modo che utilizzino eprintln! al loro posto.

File: src/main.rs
use std::env;
use std::error::Error;
use std::fs;
use std::process;

use minigrep::{cerca, cerca_case_insensitive};

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

    let config = Config::build(&args).unwrap_or_else(|err| {
        eprintln!("Problema nella lettura degli argomenti: {err}");
        process::exit(1);
    });

    if let Err(e) = esegui(config) {
        eprintln!("Errore dell'applicazione: {e}");
        process::exit(1);
    }
}

pub struct Config {
    pub query: String,
    pub percorso_file: String,
    pub ignora_maiuscole: bool,
}

impl Config {
    fn build(args: &[String]) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("non ci sono abbastanza argomenti");
        }

        let query = args[1].clone();
        let percorso_file = args[2].clone();

        let ignora_maiuscole = env::var("IGNORA_MAIUSCOLE").is_ok();

        Ok(Config {
            query,
            percorso_file,
            ignora_maiuscole,
        })
    }
}

fn esegui(config: Config) -> Result<(), Box<dyn Error>> {
    let contenuto = fs::read_to_string(config.percorso_file)?;

    let risultato = if config.ignora_maiuscole {
        cerca_case_insensitive(&config.query, &contenuto)
    } else {
        cerca(&config.query, &contenuto)
    };

    for line in risultato {
        println!("{line}");
    }

    Ok(())
}
Listato 12-24: Scrittura di messaggi di errore sullo standard error anziché sullo standard output usando eprintln!

Ora eseguiamo di nuovo il programma nello stesso modo, senza argomenti e reindirizzando lo standard output con >:

$ cargo run > output.txt
Problema nella lettura degli argomenti: non ci sono abbastanza argomenti

Ora vediamo l’errore sullo schermo e output.txt non contiene nulla, che è il comportamento che ci aspettiamo dai programmi a riga di comando.

Eseguiamo di nuovo il programma con argomenti che non causano errori ma che comunque reindirizziamo l’output standard a un file, in questo modo:

$ cargo run -- che poesia.txt > output.txt

Non vedremo alcun output sul terminale e output.txt conterrà i nostri risultati:

File: output.txt

Sei Nessuno anche tu?
che gracida il tuo nome — tutto giugno —

Questo dimostra che ora stiamo utilizzando lo standard output per l’output corretto e lo standard error per l’output dei messaggi di errore, a seconda dei casi.

Questo capitolo ha messo in pratica alcuni dei concetti principali appresi finora e ha spiegato come eseguire operazioni di I/O comuni in Rust. Utilizzando argomenti della riga di comando, file, variabili d’ambiente e la macro eprintln! per la stampa degli errori, ora sei pronto a scrivere applicazioni da riga di comando. In combinazione con i concetti dei capitoli precedenti, il codice sarà ben organizzato, memorizzerà i dati in modo efficace nelle strutture dati appropriate, gestirà gli errori in modo efficiente e sarà ben testato.

Andando avanti, esploreremo alcune funzionalità di Rust ispirate dai linguaggi funzionali: closure (chiusure) e iteratori.