Ce face Hash
HashMap și HashSet găsesc rapid o cheie calculând un număr („hash”) din ea. Tipul cheii trebuie să implementeze Hash și Eq. #[derive(Hash)] combină hash-urile tuturor câmpurilor, în ordine; merge dacă toate câmpurile sunt Hash (nu și f64).
Regula de aur: dacă a == b, atunci hash-urile lor trebuie să fie egale. Dacă implementezi manual PartialEq (de exemplu ignorând literele mari), implementează și Hash manual, folosind exact aceleași informații. Altfel două chei „egale” ajung în locuri diferite din tabel.
Valoarea concretă a hash-ului nu trebuie afișată sau salvată: HashMap folosește o cheie aleatorie la fiecare pornire a programului, pentru protecție împotriva atacurilor.
Parametri
| Parametru | Descriere |
|---|---|
hash(&self, state) |
Trimite în state datele care identifică valoarea (de obicei cu camp.hash(state)). |
Ce returnează
Nimic; datele sunt adăugate în Hasher.
Exemple
Exemplul 1: derive(Hash) pentru o cheie compusă
use std::collections::HashMap;
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord)]
struct Loc {
rand: u8,
scaun: u8,
}
fn main() {
let mut rezervari: HashMap<Loc, &str> = HashMap::new();
rezervari.insert(Loc { rand: 3, scaun: 7 }, "Ana");
rezervari.insert(Loc { rand: 1, scaun: 2 }, "Radu");
rezervari.insert(Loc { rand: 3, scaun: 7 }, "Ioana"); // înlocuiește
let mut lista: Vec<_> = rezervari.iter().collect();
lista.sort();
for (loc, nume) in lista {
println!("rândul {}, scaunul {}: {nume}", loc.rand, loc.scaun);
}
}
Rezultat
rândul 1, scaunul 2: Radu
rândul 3, scaunul 7: Ioana
Exemplul 2: Hash manual, în acord cu PartialEq
E-mailurile se compară fără să țină cont de litere mari, deci și hash-ul se calculează din forma cu litere mici. Altfel HashSet ar păstra duplicatele.
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
struct Email(String);
impl PartialEq for Email {
fn eq(&self, other: &Self) -> bool {
self.0.eq_ignore_ascii_case(&other.0)
}
}
impl Eq for Email {}
impl Hash for Email {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.to_ascii_lowercase().hash(state);
}
}
fn main() {
let adrese = ["ana@exemplu.ro", "ANA@exemplu.ro", "radu@exemplu.ro", "Ana@Exemplu.ro"];
let unice: HashSet<Email> = adrese.iter().map(|a| Email(a.to_string())).collect();
println!("{} adrese, {} unice", adrese.len(), unice.len());
println!("conține RADU@EXEMPLU.RO: {}", unice.contains(&Email("RADU@EXEMPLU.RO".into())));
}
Rezultat
4 adrese, 2 unice
conține RADU@EXEMPLU.RO: true
Greșeli frecvente
- Folosești ca cheie un tip fără
Hash:the trait bound `Tip: Hash` is not satisfied. Adaugă#[derive(PartialEq, Eq, Hash)]. - Modifici o cheie după ce a fost pusă în
HashMap(prinCell/RefCell): hash-ul nu mai corespunde, iar cheia nu mai poate fi găsită.