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.
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(())
}
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.
Riepilogo
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.