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

Ricevere Argomenti dalla Riga di Comando

Crea un nuovo progetto con, come sempre, cargo new. Chiameremo il nostro progetto minigrep per distinguerlo dallo strumento grep che potresti già avere sul tuo sistema:

$ cargo new minigrep
Created binary (application) `minigrep` project
$ cd minigrep

Il primo compito è fare in modo che minigrep accetti i suoi due argomenti della riga di comando: il percorso del file e una stringa da cercare. Cioè, vogliamo essere in grado di eseguire il nostro programma con cargo run, due trattini per indicare che i seguenti argomenti sono per il nostro programma e non per cargo, una stringa da cercare e un percorso a un file in cui cercare, in questo modo:

$ cargo run -- stringa-da-trovare file-esempio.txt

Al momento, il programma generato da cargo new non può elaborare gli argomenti che gli forniamo. Alcune librerie esistenti su crates.io possono aiutare a scrivere un programma che accetti argomenti da riga di comando, ma poiché stiamo apprendendo solo ora questo concetto, implementiamo questa funzionalità da soli.

Leggere i Valori degli Argomenti

Per consentire a minigrep di leggere i valori degli argomenti da riga di comando che gli passiamo, avremo bisogno della funzione std::env::args fornita nella libreria standard di Rust. Questa funzione restituisce un iteratore degli argomenti della riga di comando passati a minigrep. Tratteremo gli iteratori in dettaglio nel Capitolo 13. Per ora, è sufficiente conoscere solo due dettagli sugli iteratori: gli iteratori producono una serie di valori e possiamo chiamare il metodo collect su un iteratore per trasformarlo in una collezione, come un vettore, che contiene tutti gli elementi prodotti dall’iteratore.

Il codice nel Listato 12-1 consente al programma minigrep di leggere qualsiasi argomento della riga di comando passato e quindi raccogliere i valori in un vettore.

File: src/main.rs
use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    dbg!(args);
}
Listato 12-1: Raccolta degli argomenti della riga di comando in un vettore e stamparli

Per prima cosa, portiamo il modulo std::env nello scope con un’istruzione use in modo da poter utilizzare la sua funzione args. Nota che la funzione std::env::args è annidata in due livelli di moduli. Come discusso nel Capitolo 7, nei casi in cui la funzione desiderata è annidata in più di un modulo, abbiamo scelto di portare nello scope il modulo genitore anziché la funzione. In questo modo, possiamo facilmente utilizzare altre funzioni da std::env. È anche meno ambiguo rispetto all’aggiunta di use std::env::args e quindi alla chiamata della funzione con solo args, perché args potrebbe essere facilmente confuso con una funzione definita nel modulo corrente.

La Funzione args e Unicode non Valido

Nota che std::env::args andrà in panic se un argomento contiene Unicode non valido. Se il programma deve accettare argomenti contenenti Unicode non valido, utilizzare invece std::env::args_os. Questa funzione restituisce un iteratore che produce valori OsString invece di valori String. Abbiamo scelto di utilizzare std::env::args qui per semplicità perché i valori OsString variano a seconda della piattaforma e sono più complessi da gestire rispetto ai valori String.

Nella prima riga del corpo di main, chiamiamo env::args e utilizziamo immediatamente collect per trasformare l’iteratore in un vettore contenente tutti i valori prodotti dall’iteratore. Possiamo usare la funzione collect per creare molti tipi di collezioni, quindi annotiamo esplicitamente il type di args per specificare che vogliamo un vettore di stringhe. Sebbene sia molto raro dover annotare i type in Rust, collect è una funzione che spesso occorre annotare perché Rust non è in grado di dedurre il tipo di collezione desiderata.

Infine, stampiamo il vettore usando la macro di debug. Proviamo a eseguire il codice prima senza argomenti e poi con due argomenti:

$ cargo run
   Compiling minigrep v0.1.0 (file:///progetti/minigrep)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.61s
     Running `target/debug/minigrep`
[src/main.rs:5:5] args = [
    "target/debug/minigrep",
]
$ cargo run -- ago pagliaio
   Compiling minigrep v0.1.0 (file:///progetti/minigrep)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.57s
     Running `target/debug/minigrep ago pagliaio`
[src/main.rs:5:5] args = [
    "target/debug/minigrep",
    "ago",
    "pagliaio",
]

Nota che il primo valore nel vettore è "target/debug/minigrep", che è il nome del nostro binario. Questo corrisponde al comportamento dell’elenco degli argomenti in C, consentendo ai programmi di utilizzare il nome con cui sono stati invocati durante l’esecuzione. Spesso è comodo avere accesso al nome del programma nel caso in cui si voglia visualizzarlo nei messaggi o modificarne il comportamento in base all’alias della riga di comando utilizzato per invocarlo. Ma ai fini di questo capitolo, lo ignoreremo e salveremo solo i due argomenti di cui abbiamo bisogno.

Salvare i Valori degli Argomenti nelle Variabili

Il programma è attualmente in grado di accedere ai valori specificati come argomenti della riga di comando. Ora dobbiamo salvare i valori dei due argomenti nelle variabili in modo da poterli utilizzare nel resto del programma. Lo facciamo nel Listato 12-2.

File: src/main.rs
use std::env;

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

    let query = &args[1];
    let percorso_file = &args[2];

    println!("Cerco {query}");
    println!("Nel file {percorso_file}");
}
Listato 12-2: Creazione di variabili per contenere l’argomento query e l’argomento percorso_file

Come abbiamo visto quando abbiamo stampato il vettore, il nome del programma occupa il primo valore nel vettore in args[0], quindi gli argomenti che servono a noi iniziano dall’indice 1. Il primo argomento preso da minigrep è la stringa che stiamo cercando, quindi inseriamo un reference al primo argomento nella variabile query. Il secondo argomento sarà il percorso del file, quindi inseriamo un reference al secondo argomento nella variabile percorso_file.

Stampiamo temporaneamente i valori di queste variabili per dimostrare che il codice funziona come previsto. Eseguiamo di nuovo questo programma con gli argomenti test e esempio.txt:

$ cargo run -- test esempio.txt
   Compiling minigrep v0.1.0 (file:///progetti/minigrep)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.0s
     Running `target/debug/minigrep test esempio.txt`
Cerco test
Nel file esempio.txt

Ottimo, il programma funziona! I valori degli argomenti di cui abbiamo bisogno vengono salvati nelle variabili corrette. In seguito aggiungeremo una gestione degli errori per gestire alcune potenziali situazioni errate, come quando l’utente non fornisce argomenti; per ora ignoreremo questa situazione e lavoreremo invece sull’aggiunta di funzionalità per la lettura dei file.