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.
use std::env; fn main() { let args: Vec<String> = env::args().collect(); dbg!(args); }
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.
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}");
}
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.