Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Rust e strutture dati avanzate

Rust: parliamo di strutture dati avanzate e delle modalità con cui i dati possono essere letti e scritti al loro interno
Rust: parliamo di strutture dati avanzate e delle modalità con cui i dati possono essere letti e scritti al loro interno
Link copiato negli appunti

Le strutture dati sono necessarie in qualsiasi forma di programmazione, compresa quella in Rust. Dopo le variabili costituiscono il modo per poter organizzare le informazioni sia internamente sia tra di loro, permettendo di costruire delle vere e proprie reti da analizzare. Ne abbiamo già incontrate tre fondamentali in questa guida: array, tuple e vettori.

Queste rispondono perfettamente ad alcune esigenze ma per poter realizzare un programma completo ce ne servono altre. Le strutture dati non sono fatte solo di dati ma anche di politiche di gestione ovvero le modalità in cui i dati possono al loro interno essere letti e scritti. In base a queste, sapremo quale sarà la struttura dati giusta da impiegare in un determinato contesto.

Politiche di gestione in Rust

Le politiche di gestione principali sono tre:

  • accesso sequenziale: i dati vengono gestiti in base alla posizione che permette di distinguerli gli uni dagli altri (sono pertanto ammessi i duplicati).
    Questa esigenza è soddisfatta dalle strutture già citate ovvero array, vettori e tuple;
  • accesso chiave/valore: ogni dato non viene inserito da solo ma accompagnato da una chiave che serve per recuperarlo istantaneamente;
  • valori singoli senza duplicati: è il caso dei set.

In questa lezione ci occuperemo di imparare ulteriori strutture dati oltre a quelle già viste in modo da avere un ventaglio di scelte completo per affrontare ogni casistica che si possa presentare nei nostri programmi. In particolare, ci concentreremo sul secondo e terzo approccio dell'elenco precedente: chiave/valore e set.

HashMap: gestione dei dati chiave/valore

Il modo più immediato per lavorare con l'accesso chiave valore consiste nell'uso di una HashMap importabile con:

use std::collections::HashMap;

accedendo alle collections della libreria standard.

Come primo esperimento si può iniziare a creare una HashMap ed inserirvi alcuni elementi tenendo ben presente che andremo ad immettere sempre una coppia ovvero un valore accompagnato da una chiave. Qui supporremo di registrare allievi con i loro voti: la chiave sarà una stringa contenente cognome e nome dell'allievo ed il valore un numero in virgola per rappresentare il voto:

let mut allievi: HashMap<String, f32> = HashMap::new();
allievi.insert(String::from("Rossi Enzo"), 6.5);
allievi.insert(String::from("Bianchi Ilenia"), 8.0);
println!("HashMap = {:?}", allievi);

Abbiamo creato una struttura mutabile e con il metodo insert vi abbiamo inserito una coppia alla volta. L'output che si ottiene è il seguente:

HashMap = {"Rossi Enzo": 6.5, "Bianchi Ilenia": 8.0}

L'operazione di lettura richiederà la presentazione di una chiave per ottenere, in risposta, il valore corrispondente. Ad esempio, che voto ha preso Ilenia? Chiediamolo alla HashMap:

println!("Voti di Ilenia = {:?}", allievi.get("Bianchi Ilenia"));

Avremo come risposta:

Voti di Ilenia = Some(8.0)

Notiamo che nella stampa "diretta" che si è ottenuta è stato presentato il valore come Some in quanto non vi è certezza della presenza della chiave richiesta. In alternativa, in caso di chiave non esistente, avremmo ottenuto - come abbiamo già imparato - un None.

Altre funzioni per la manipolazione in Rust

Esistono inoltre altre funzioni per la manipolazione di questi costrutti che avremo modo di sperimentare nelle prossime esercitazioni. Tra questi:

  • remove per la rimozione di un elemento in base alla chiave;
  • len per conoscere il numero di accoppiate inserite;
  • keys per ottenere l'elenco delle chiavi;
  • values per avere un elenco dei valori.

Set: collezioni senza duplicati

Spesso - non stiamo assolutamente parlando di un caso di nicchia - è fondamentale collezionare elementi senza duplicati. Se ad esempio stiamo cercando URL di pagine web e vogliamo inserirle in maniera unica all'interno di una struttura dati, con i vettori dovremmo, ad ogni inserimento, verificare che l'elemento non sia già presente. In tali casi, avere una struttura dati che non ammette duplicati è fondamentale perché, alla seconda immissione del medesimo valore, questo verrebbe ignorato senza bisogno di alcun controllo da parte nostra con un bel ritorno in termini di produttività ed efficienza. Impariamo ad usare questi costrutti in Rust con l'HashSet:

use std::collections::HashSet;
fn main() {
    let mut frutti: HashSet<&str> = HashSet::new();
    frutti.insert("banana");
    frutti.insert("mela");
    frutti.insert("pera");
    frutti.insert("mela");
    frutti.insert("pesca");
    frutti.insert("banana");
    frutti.insert("mela");
    println!("Frutti presenti = {:?}", frutti);
    if frutti.contains("mela") {
        println!("Abbiamo la mela")
    }
    else {
        println!("La mela manca")
    }
}

L'esempio ricalca quello precedente sulle "mappe" prevedendo la creazione di un HashSet ed inserendovi degli elementi. Come si vede, abbiamo provato ad inserire più termini ripetuti (mela e banana) che sono però stati accettati una sola volta infatti l'output del primo println equivale a Frutti presenti = {"banana", "pera", "mela", "pesca"}. Il blocco if che segue mostra come poter verificare che un valore sia presente: in questo caso, la mela c'è!

Anche i set sono forniti di molti metodi che possono essere sfruttati per le varie operazioni. Al momento, possiamo citare come principali strumenti utili:

  • remove per la rimozione di elementi;
  • union per l'operazione insiemistica di unione di insiemi;
  • intersection per l'operazione insiemistica di intersezione di insiemi;
  • difference per l'operazione insiemistica di differenza di insiemi.

Anche in questo caso rimandiamo ulteriori approfondimenti ai prossimi esercizi che faremo insieme.

Ti consigliamo anche