Shopping cart

0

Cart

  • 0 item

Nessun prodotto nel carrello.

All categories

SE VUOI PRENDERE LA CERTIFICAZIONE PER QUESTO CORSO CLICCA QUI

Utilizzo dei Pattern in Rust

I pattern sono utilizzati in diversi contesti in Rust, e li hai usati molto senza accorgertene! In questa sezione parleremo di tutti i luoghi dove i pattern sono validi.

Bracci di match

Come discusso nel Capitolo 6, usiamo i pattern nei bracci delle espressioni di match. Formalmente, le espressioni di match sono definite dalla parola chiave match, un valore su cui fare il match, e uno o più bracci di match che consistono in un pattern e un’espressione da eseguire se il valore fa match con quel pattern, così:

rust

match VALORE {
PATTERN => ESPRESSIONE,
PATTERN => ESPRESSIONE,
PATTERN => ESPRESSIONE,
}

Ad esempio, ecco un’espressione di match dal Listato 6-5 che fa match su un valore di tipo Option<i32> nella variabile x:

rust

match x {
None => None,
Some(i) => Some(i + 1),
}

I pattern in questa espressione di match sono None e Some(i) a sinistra di ogni freccia.

Un requisito per le espressioni di match è che devono essere esaustive nel senso che tutte le possibilità per il valore nell’espressione di match devono essere considerate. Un modo per assicurarsi di aver coperto ogni possibilità è avere un pattern “catchall” per l’ultimo braccio: ad esempio, un nome di variabile che fa match con qualsiasi valore può coprire tutti i casi rimanenti.

Il particolare pattern _ farà match con qualsiasi cosa, ma non si legherà mai a una variabile, quindi è spesso usato nell’ultimo braccio di match. Il pattern _ può essere utile quando si desidera ignorare qualsiasi valore non specificato, ad esempio.

Espressioni condizionali if let

Nel Capitolo 6 abbiamo discusso come usare le espressioni if let principalmente come un modo più breve per scrivere l’equivalente di un match che fa match solo con un caso. Opzionalmente, if let può avere un else corrispondente contenente del codice da eseguire se il pattern nell’if let non fa match.

Il Listato 18-1 mostra che è anche possibile mescolare e abbinare espressioni if let, else if e else if let. Farlo ci dà più flessibilità rispetto a un’espressione di match, in cui possiamo confrontare solo un valore con i pattern.

Il codice nel Listato 18-1 determina quale colore dare allo sfondo in base a una serie di controlli per diverse condizioni.

Se l’utente specifica un colore preferito, quel colore è usato come sfondo. Se non viene specificato alcun colore preferito e oggi è martedì, lo sfondo è verde. Altrimenti, se l’utente specifica la propria età come una stringa e riusciamo a convertirla con successo in un numero, il colore è viola o arancione a seconda del valore del numero. Se nessuna di queste condizioni si applica, lo sfondo è blu.

Questa struttura condizionale ci consente di supportare requisiti complessi. Con i valori hard-coded che abbiamo qui, questo esempio stamperà “Using purple as the background color”.

Si può notare che if let può anche introdurre variabili ombreggiate allo stesso modo dei bracci di match: la linea if let Ok(age) = age introduce una nuova variabile ombreggiata age che contiene il valore all’interno della variante Ok. Questo significa che dobbiamo inserire la condizione if age > 30 all’interno di quel blocco: non possiamo combinare queste due condizioni in if let Ok(age) = age && age > 30. L’età ombreggiata che vogliamo confrontare con 30 non è valida fino a quando non inizia il nuovo ambito con la graffa.

Lo svantaggio nell’utilizzare le espressioni if let è che il compilatore non controlla l’esauribilità, mentre con le espressioni di match lo fa. Se omettessimo l’ultimo blocco else e quindi non gestissimo alcuni casi, il compilatore non ci avviserebbe del possibile bug logico.

Cicli Condizionali while let

Simile nella struttura all’if let, il ciclo condizionale while let consente a un ciclo while di continuare ad eseguirsi finché un pattern continua a fare match. Nel Listato 18-2 scriviamo un ciclo while let che utilizza un vettore come stack e stampa i valori nel vettore nell’ordine opposto in cui sono stati inseriti.

rust

let mut stack = Vec::new(); stack.push(1);
stack.push(2);
stack.push(3);

while let Some(top) = stack.pop() {
println!("{}", top);
}

Questo esempio stampa 3, 2 e poi 1. Il metodo pop prende l’ultimo elemento dal vettore e restituisce Some(value). Se il vettore è vuoto, pop restituisce None. Il ciclo while continua a eseguire il codice nel suo blocco fintanto che pop restituisce Some. Quando pop restituisce None, il ciclo si ferma. Possiamo usare while let per rimuovere ogni elemento dallo stack.

Cicli for

In un ciclo for, il valore che segue direttamente la parola chiave for è un pattern. Ad esempio, in for x in y il x è il pattern. Il Listato 18-3 mostra come utilizzare un pattern in un ciclo for per destrutturare, o spezzare, una tupla come parte del ciclo for.

rust

let v = vec!['a', 'b', 'c'];

for (index, value) in v.iter().enumerate() {
println!("{} is at index {}", value, index);
}

Il codice nel Listato 18-3 stamperà:

csharp

a is at index 0
b is at index 1
c is at index 2

Adattiamo un iteratore utilizzando il metodo enumerate in modo che produca un valore e l’indice per quel valore, inserito in una tupla. Il primo valore prodotto è la tupla (0, ‘a’). Quando questo valore fa match con il pattern (index, value), index sarà 0 e value sarà ‘a’, stampando la prima riga dell’output.

Dichiarazioni let

Prima di questo capitolo, avevamo discusso esplicitamente l’utilizzo di pattern con match e if let, ma in realtà abbiamo utilizzato pattern anche in altri contesti, inclusi nelle dichiarazioni let. Ad esempio, considera questa assegnazione di variabile diretta con let:

rust

let x = 5;

Ogni volta che hai usato una dichiarazione let come questa hai usato pattern, anche se potresti non essertene accorto! Più formalmente, una dichiarazione let appare così:

rust

let PATTERN = ESPRESSIONE;

In dichiarazioni come let x = 5; con un nome di variabile nel posto del pattern, il nome della variabile è solo una forma particolarmente semplice di un pattern. Rust confronta l’espressione con il pattern e assegna eventuali nomi che trova. Quindi, nell’esempio let x = 5;, x è un pattern che significa “lega ciò che fa match qui alla variabile x“. Poiché il nome x è l’intero pattern, questo pattern significa effettivamente “lega tutto alla variabile x, qualunque sia il valore”.

Per vedere più chiaramente l’aspetto del matching dei pattern di let, considera il Listato 18-4, che utilizza un pattern con let per destrutturare una tupla.

rust

let (x, y, z) = (1, 2, 3);

Qui, facciamo match di una tupla con un pattern. Rust confronta il valore (1, 2, 3) con il pattern (x, y, z) e vede che il valore fa match con il pattern, quindi Rust lega 1 a x, 2 a y e 3 a z. Puoi pensare a questo pattern di tupla come a tre pattern di variabili individuali nidificati al suo interno.

1 Comment

Leave a Reply

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *