Entry::or_default în Rust

Dacă cheia lipsește, inserează valoarea implicită a tipului (0, vector gol, text gol) și returnează &mut V.

Sintaxă
pub fn or_default(self) -> &'a mut V
where V: Default

Ce face Entry::or_default

or_default() este or_insert_with(Default::default), scris scurt. Valoarea implicită depinde de tipul valorilor: 0 pentru numere, false pentru bool, Vec::new(), String::new(), un HashMap gol etc. Tipul trebuie să implementeze Default.

Este forma cea mai scurtă pentru contoare (*m.entry(k).or_default() += 1) și pentru grupare (m.entry(k).or_default().push(x)). Funcționează la fel pe BTreeMap.

Ce returnează

&mut V.

Exemple

Exemplul 1: Contor de cuvinte, cu BTreeMap pentru ordine

BTreeMap are același entry; cheile ies sortate fără efort suplimentar.

use std::collections::BTreeMap;

fn main() {
    let text = "mere pere mere prune pere mere caise";
    let mut numar: BTreeMap<&str, u32> = BTreeMap::new();
    for cuvant in text.split_whitespace() {
        *numar.entry(cuvant).or_default() += 1;
    }
    println!("{:?}", numar);
}

Rezultat

{"caise": 1, "mere": 3, "pere": 2, "prune": 1}

Exemplul 2: Note grupate pe materii

use std::collections::HashMap;

fn main() {
    let note = [("matematică", 9), ("română", 10), ("matematică", 7), ("fizică", 8), ("română", 9)];
    let mut pe_materii: HashMap<&str, Vec<u32>> = HashMap::new();
    for (materie, nota) in note {
        pe_materii.entry(materie).or_default().push(nota);
    }
    let mut materii: Vec<_> = pe_materii.iter().collect();
    materii.sort();
    for (materie, lista) in materii {
        let media = lista.iter().sum::<u32>() as f64 / lista.len() as f64;
        println!("{materie:11} {:?} media {media:.2}", lista);
    }
}

Rezultat

fizică      [8] media 8.00
matematică  [9, 7] media 8.00
română      [10, 9] media 9.50

Exemplul 3: Dicționar în dicționar: vânzări pe oraș și produs

Primul or_default() creează dicționarul interior pentru oraș, al doilea creează contorul pentru produs.

use std::collections::BTreeMap;

fn main() {
    let vanzari = [("Iași", "pâine", 3), ("Cluj", "lapte", 2), ("Iași", "lapte", 1), ("Iași", "pâine", 2)];
    let mut tabel: BTreeMap<&str, BTreeMap<&str, u32>> = BTreeMap::new();
    for (oras, produs, buc) in vanzari {
        *tabel.entry(oras).or_default().entry(produs).or_default() += buc;
    }
    for (oras, produse) in &tabel {
        println!("{oras}: {:?}", produse);
    }
}

Rezultat

Cluj: {"lapte": 2}
Iași: {"lapte": 1, "pâine": 5}

Greșeli frecvente

  • Valori de un tip fără Default (de exemplu o structură proprie fără #[derive(Default)]): the trait bound `T: Default` is not satisfied. Adaugă derive-ul sau folosește or_insert_with.
  • Te aștepți la altă valoare inițială decât cea implicită (de exemplu 1). Pentru asta folosește or_insert(1).

Comenzi înrudite

  • Entry::or_insert: Dacă cheia lipsește, inserează valoarea dată; în ambele cazuri returnează o referință mutabilă la valoarea din dicționar.
  • Entry::or_insert_with: Ca or_insert, dar valoarea de inserat e calculată de un closure, apelat doar dacă cheia lipsește.
  • HashMap::entry: Caută o cheie o singură dată și îți dă „locul” ei (Entry), ca să inserezi, să citești sau să modifici fără o a doua căutare.
  • BTreeMap: Dicționar cu cheile mereu sortate: aceleași operații ca HashMap, dar parcurgerea și afișarea sunt în ordinea cheilor.