|
Proseguendo nel nostro viaggio tra i tipi proposti da Rust è arrivato il momento di parlare degli slice, che avevamo sfiorato parlando delle sequenze letterali, in maniera approfondita. Argomento fondamentale per questo linguaggio, si tratta in breve di un riferimento ad una sequenza contigua di elementi in memoria. Gli slice sono particolarmente utili perché offrono un modo per accedere a una porzione di un array o di un altro slice senza prendere possesso di esso, permettendo così operazioni sicure e efficienti su sottosequenze di una collezione di dati. Inoltre uno dei fatti essenzialie delle slice è che rappresentano una astrazione a costo zero in Rust. Quando lavoriamo con una slice, Rust non introduce overhead aggiuntivo a runtime rispetto a lavorare direttamente con gli array. Ciò è garantito dal fatto che il compilatore genera codice ottimizzato come se stessimo lavorando direttamente sugli array. In pratica le slice in Rust sono progettate per essere sicure e performanti. Il compilatore garantisce che siano sempre valide e non causino errori di accesso alla memoria. Semanticamente abbiamo due tipi di slice: &[T] &mut [T] - slice mutabili dove T ovviamente rappresenta il tipo. Come troverete scritto nella letteratura dedicata, i vantaggi degli slice si possono rissumere in:
Come avevamo già visto, componente fondamentale degli slice è l'operatore & che in questo contesto rappresenta come detto un puntatore, un riferimento, alla sequenza indicata ciò che permette la realizzazione del meccanismo di borrowing. Nel caso delle sequenze letterali avevamo una string slice ma adesso scopriamo che l'applicazione è molto più ampia. Questo è un altro dei punti di forza di questo costrutto. Prima di addentrarci in esempi applicativi vediamo come lavorare con i nsotri slice. Iiniziamo con una paio di metodi di base molto uitili, specialmente il primo:: len - che indica il numero di elementi dello slice is_empty - che riporta se lo slice è vuoto oppure no
let ar01 = [1, 2, 3, 4, 5];
let sl01 = &ar01[1..4];
println!("Elementi in slice: {}", sl01.len());
println!("Slice vuoto? {}", sl01.is_empty());
Il risultato della prima istruzione è 3 in quanto il range non è inclusivo (per un range inclusivo dovremmo scrivere 1..=4 ma ne parleremo nel paragrafo dedicato ai range) e quindi lo slice prenderà gli elementi con indice 1,2,3 costruendo la sequenza costituita dai numeri 2 3 4 che avranno indice 0 1 2. Per accedere ad un singolo elemento si può usare l'operatore [] Riprendendo l'esempio precedente:
let ar01 = [1, 2, 3, 4, 5];
let sl01 = &ar01[1..4];
println!("{}", sl01[2]);
ci restituirà il numero 4. Per iterare attraverso gli elementi di uno slice abbiamo iter
Usando iter non possiamo modificare nulla dello slice, è un'operazione di sola lettura. Un altro metodo utile è contains(&T) che permette di verificare l'esistenza di un dato elemento all'interno dello slice dando come risultato true o false a seconda l'elemento cercato sia oppure no nello slice:
let sl01 = &[1, 2, 3, 4, 5]; // Definiamo uno slice
if sl01.contains(&3) {
println!("Lo slice contiene l'elemento {}!", 3);
} else {
println!("L'elemento {} non è presente nello slice.",
3);
}
Invece sort e reverse possono aiutare ad ordinare come preferite i vostri dati:
e abbiamo:
Scopriamo ora come lavorare per cambiare gli elementi di uno slice:
che ci dà:
fin qui tutto normale, abbiamo dichiarato uno slice mutabile e lo abbiamo di fatto mutato. let ar01 = [7,5,9,1,0]; let sl01 = &ar01; println!("{:?}", sl01); Questo primo step costruisce uno slice che punta ad un array. Ovviamente niente può essere cambiato. let ar01 = [7,5,9,1,0]; let sl01 = &mut ar01; println!("{:?}", sl01); sl01[0] = 77; Questo step non funziona. Il motivo è che dichiariamo di poter cambiare un elemento di qualcosa che, nella prima riga non è dicharato mutabile. Il compilatore ce lo dice senza mezzi termini: error[E0596]: cannot borrow `ar01` as mutable, as it is not declared as mutable --> r199.rs:3:16 | 3 | let sl01 = &mut ar01; | ^^^^^^^^^ cannot borrow as mutable | help: consider changing this to be mutable | 2 | let mut ar01 = [7,5,9,1,0]; | +++ quindi: let mut ar01 = [7,5,9,1,0]; let sl01 = &mut ar01; sl01[0] = 77; println!("{:?}", sl01); ed otteniamo lo stesso risultato dell'esempio precedente. Come per gli array avremo un panic in caso di indice fuori range. La soluzione è la stessa già vista per quelle sequenze, ovvero get_mut(indice) che restituirà un Option. Vi lascio il semplice adattamento dell'esempio., riprendendo quello del capitolo precedente. |