SE VUOI PRENDERE LA CERTIFICAZIONE PER QUESTO CORSO CLICCA QUI
Il Tipo Slice
Gli slices ti permettono di fare riferimento a una sequenza contigua di elementi in una collezione anziché all’intera collezione. Un slice è una sorta di riferimento, quindi non possiede i dati.
Immagina di dover scrivere una funzione che trova la prima parola in una frase. Se la frase ha più parole separate da spazi, la funzione dovrebbe restituire solo la prima parola.
Iniziamo con una funzione che usa l’intera Stringa:
rust
fn first_word(s: &String) -> usize {
let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return i;
}
}
s.len()
}
Questa funzione controlla ogni carattere nella stringa finché non trova uno spazio. Restituisce quindi la posizione dello spazio, che corrisponde alla fine della prima parola. Se non trova spazi, restituisce la lunghezza totale della stringa, perché significa che la frase è composta da una sola parola.
Il problema di questa funzione è che restituisce un numero che rappresenta la posizione della fine della parola. Questo numero è valido solo per la stringa attuale. Se cambi la stringa dopo aver usato questa funzione, quel numero potrebbe non avere più senso. Per esempio:
rust
let mut s = String::from("ciao mondo");
let parola = first_word(&s); // "parola" avrà il valore 4
s.clear(); // ora la stringa è vuota
Dopo aver cancellato la stringa, il valore restituito da first_word
non ha più senso perché non c’è più alcuna parola nella stringa. Questo può portare a bug difficili da individuare.
La soluzione a questo problema sono gli slices di stringhe. Gli slices permettono di fare riferimento solo a una parte della stringa originale, mantenendo un collegamento alla stringa completa. Così, se la stringa cambia, l’slice cambia automaticamente per riflettere la modifica.
Un altro vantaggio degli slices è che possono essere utilizzati sia con le Stringhe che con le stringhe letterali. Le stringhe letterali sono sequenze di caratteri scritte direttamente nel codice e sono immutabili.
rust
let s = String::from("ciao mondo");
let parola = &s[0..4]; // "parola" è uno slice della Stringa "ciao mondo"
In questo caso, “parola” è uno slice che punta alla parte di “ciao mondo” fino al quarto carattere, quindi “ciao”. Se modifichi la stringa originale, lo slice si adatta automaticamente. Ad esempio:
rust
let mut s = String::from("ciao mondo");
let parola = &s[0..4]; // "parola" punta a "ciao"
s.clear(); // ora la stringa è vuota
println!("La parola è: {}", parola); // Stampa "La parola è: "
Anche se hai cancellato la stringa, lo slice si è aggiornato di conseguenza. Questo rende gli slices molto più sicuri e affidabili rispetto alla semplice indicizzazione dei caratteri.
Stringhe Letterali come Slices
Ricorda quando abbiamo parlato delle stringhe letterali che vengono memorizzate all’interno del binario? Ora, con la conoscenza degli slices, possiamo capire meglio le stringhe letterali:
rust
let s = "Ciao, mondo!";
Il tipo di s
qui è &str
: è uno slice che punta a quel punto specifico del binario. Questo è anche il motivo per cui le stringhe letterali sono immutabili; &str
è un riferimento immutabile.
Slices di Stringhe come Parametri
Sapendo che è possibile prendere slices di stringhe letterali e valori Stringa ci porta a un’altra miglioria per first_word
, ed è la sua firma:
rust
fn first_word(s: &String) -> &str {
Un Rustacean più esperto scriverebbe invece la firma mostrata nel Listato 4-9 perché ci permette di usare la stessa funzione sia con valori &String
che con valori &str
.
rust
fn first_word(s: &str) -> &str {
Se abbiamo uno slice di stringa, possiamo passarlo direttamente. Se abbiamo una Stringa, possiamo passare uno slice della Stringa o un riferimento alla Stringa. Questa flessibilità sfrutta le coercizioni di dereferenziazione, una funzionalità che tratteremo nella sezione “Coercizioni di Dereferenziazione Implicite con Funzioni e Metodi” del Capitolo 15.
Definire una funzione per prendere uno slice di stringa invece di un riferimento a una Stringa rende la nostra API più generale e utile senza perdere alcuna funzionalità.