HashMap in Rust
Una HashMap salva i dati come coppie chiave-valore: cerchi le cose tramite una chiave invece che tramite una posizione, come faresti con un array o un vettore. Se hai usato un dizionario in Python o un oggetto usato come mappa in JavaScript, l’idea è la stessa.
Creare una HashMap
Sezione intitolata “Creare una HashMap”use std::collections::HashMap;
fn main() { let mut ages = HashMap::new();
ages.insert("Alice", 30); ages.insert("Bob", 25);
println!("{:?}", ages);}use std::collections::HashMap; in cima porta il tipo nello scope: a differenza di Vec e String, HashMap non è disponibile di default, quindi questa riga è obbligatoria.
Cercare un valore
Sezione intitolata “Cercare un valore”use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30);
match ages.get("Alice") { Some(age) => println!("Alice ha {age} anni"), None => println!("Non trovata"), }}.get() restituisce un Option invece del valore diretto, perché la chiave potrebbe non esistere. Vedremo Option per bene nella sua pagina dedicata: per ora, match con Some/None è il modo standard per gestirlo.
Aggiornare un valore
Sezione intitolata “Aggiornare un valore”Inserire di nuovo con la stessa chiave sovrascrive il valore vecchio:
use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30); ages.insert("Alice", 31); // sovrascrive 30
println!("{:?}", ages.get("Alice"));}Se vuoi inserire un valore solo quando la chiave non esiste ancora, usa .entry():
use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30);
ages.entry("Alice").or_insert(99); // Alice esiste, quindi non fa niente ages.entry("Bob").or_insert(25); // Bob non esiste, quindi lo inserisce
println!("{:?}", ages);}Scorrere una HashMap
Sezione intitolata “Scorrere una HashMap”use std::collections::HashMap;
fn main() { let mut ages = HashMap::new(); ages.insert("Alice", 30); ages.insert("Bob", 25);
for (name, age) in &ages { println!("{name} ha {age} anni"); }}Quando usare una HashMap
Sezione intitolata “Quando usare una HashMap”Usa una HashMap ogni volta che vuoi cercare le cose per nome o per ID invece che per posizione: contare quante volte compare una parola, associare i nomi utente ai dati degli utenti, tenere in cache dei risultati per chiave. Se l’ordine conta o stai solo lavorando su una semplice lista, un Vec è la scelta migliore.
Pratica
Sezione intitolata “Pratica”1. Una piccola rubrica
Sezione intitolata “1. Una piccola rubrica”Salva tre nomi e numeri di telefono in una HashMap. Poi cerca "Ben" (che c’è) e "Dana" (che non c’è), e stampa una riga gentile per ciascuno. Suggerimento: scorri ["Ben", "Dana"] e fai match su .get(name).
Mostra la soluzione
use std::collections::HashMap;
fn main() { let mut phone_book = HashMap::new(); phone_book.insert("Ana", "555-1234"); phone_book.insert("Ben", "555-9876"); phone_book.insert("Chen", "555-4321");
for name in ["Ben", "Dana"] { match phone_book.get(name) { Some(number) => println!("{name}: {number}"), None => println!("{name}: non è in rubrica"), } }}Output:
Ben: 555-9876Dana: non è in rubricaScorrere i nomi che ci interessano, invece della mappa, ci dà anche un ordine fisso. È un trucco comodo ogni volta che ti serve un output prevedibile da una HashMap.
2. Conta i voti
Sezione intitolata “2. Conta i voti”Hai i voti ["rosso", "blu", "rosso", "verde", "blu", "rosso"]. Conta quanti ne ha presi ogni colore, poi stampa i totali di "rosso", "verde", "blu" e "rosa" (che non ne ha presi). Suggerimento: counts.entry(color).or_insert(0) ti dà il conteggio attuale di quel colore, partendo da 0. Aggiungigli uno con *count += 1.
Mostra la soluzione
use std::collections::HashMap;
fn main() { let votes = ["rosso", "blu", "rosso", "verde", "blu", "rosso"]; let mut counts = HashMap::new();
for color in votes { let count = counts.entry(color).or_insert(0); *count += 1; }
for color in ["rosso", "verde", "blu", "rosa"] { match counts.get(color) { Some(n) => println!("{color}: {n}"), None => println!("{color}: 0"), } }}Output:
rosso: 3verde: 1blu: 2rosa: 0or_insert(0) restituisce un riferimento mutabile al valore salvato nella mappa, non una copia. L’* in *count += 1 significa «modifica il numero a cui punta questo riferimento», quindi viene aggiornata la mappa stessa. Questo schema «entry, oppure inserisci 0, poi aggiungi 1» è il modo standard per contare le cose in Rust.
Riepilogo
Sezione intitolata “Riepilogo”HashMap<K, V>salva coppie chiave-valore; la porti nello scope conuse std::collections::HashMap;..insert(chiave, valore)aggiunge o sovrascrive;.get(chiave)cerca e restituisce unOption..entry(chiave).or_insert(valore)inserisce solo se la chiave non c’è già.- L’ordine di iterazione non è garantito: non farci affidamento.
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. Che cosa ti serve per portare HashMap nello scope, a differenza di Vec o String?
2. Che cosa fa .entry(key).or_insert(value)?
3. Puoi fare affidamento sull'ordine in cui vengono stampate le voci quando scorri una HashMap?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- Storing Keys with Associated Values in Hash Maps — The Rust Book (in inglese): parla dell’ownership di chiavi e valori, con un esempio di conteggio delle parole che aggiorna i valori.
- HashMap — Rust by Example (in inglese): un esempio eseguibile di rubrica con
insert,get,removee cicli. HashMap— documentazione della libreria standard (in inglese): tutti i metodi, compresicontains_key,remove,keysevalues.Entry— documentazione della libreria standard (in inglese): tutto quello che puoi fare con.entry(), comeor_insert_witheand_modify.