Salta ai contenuti

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.

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.

▶ Provalo tu
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"),
}
}
▶ Provalo tu

.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.

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"));
}
▶ Provalo tu

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);
}
▶ Provalo tu
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");
}
}
▶ Provalo tu

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.

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-9876
Dana: non è in rubrica
▶ Provalo tu

Scorrere 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.

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: 3
verde: 1
blu: 2
rosa: 0
▶ Provalo tu

or_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.

  • HashMap<K, V> salva coppie chiave-valore; la porti nello scope con use std::collections::HashMap;.
  • .insert(chiave, valore) aggiunge o sovrascrive; .get(chiave) cerca e restituisce un Option.
  • .entry(chiave).or_insert(valore) inserisce solo se la chiave non c’è già.
  • L’ordine di iterazione non è garantito: non farci affidamento.

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