Entry::and_modify în Rust

Dacă cheia există, aplică funcția dată pe valoarea ei; returnează Entry-ul, ca să poți continua cu or_insert.

Sintaxă
pub fn and_modify<F>(self, f: F) -> Entry<'a, K, V>
where F: FnOnce(&mut V)

Ce face Entry::and_modify

and_modify(|v| ...) rulează closure-ul doar pentru o cheie existentă (Occupied); pentru o cheie lipsă nu face nimic. Returnează tot un Entry, deci se înlănțuiește de obicei cu or_insert: m.entry(k).and_modify(|n| *n += 1).or_insert(1).

Forma aceasta e utilă când valoarea inițială diferă de modificare: la prima apariție pui ceva, la următoarele actualizezi altfel (de exemplu minim și maxim, prima și ultima dată). Pentru un simplu contor, *m.entry(k).or_insert(0) += 1 e la fel de bun.

Parametri

Parametru Descriere
f Closure care primește &mut V și modifică valoarea.

Ce returnează

Același Entry, pentru înlănțuire.

Exemple

Exemplul 1: Contor cu and_modify + or_insert

use std::collections::HashMap;

fn main() {
    let mut voturi: HashMap<&str, u32> = HashMap::new();
    for v in ["Ana", "Mihai", "Ana", "Ioana", "Ana", "Mihai"] {
        voturi.entry(v).and_modify(|n| *n += 1).or_insert(1);
    }
    let mut rezultat: Vec<_> = voturi.into_iter().collect();
    rezultat.sort_by(|a, b| b.1.cmp(&a.1));
    println!("{:?}", rezultat);
}

Rezultat

[("Ana", 3), ("Mihai", 2), ("Ioana", 1)]

Exemplul 2: Prețul minim și maxim al fiecărui produs

La prima apariție inserăm (preț, preț); la următoarele actualizăm capetele intervalului.

use std::collections::HashMap;

fn main() {
    let citiri = [("lapte", 8.5), ("pâine", 5.0), ("lapte", 7.9), ("lapte", 9.2), ("pâine", 5.5)];
    let mut interval: HashMap<&str, (f64, f64)> = HashMap::new();
    for (produs, pret) in citiri {
        interval
            .entry(produs)
            .and_modify(|(min, max)| {
                *min = min.min(pret);
                *max = max.max(pret);
            })
            .or_insert((pret, pret));
    }
    let mut lista: Vec<_> = interval.into_iter().collect();
    lista.sort_by(|a, b| a.0.cmp(b.0));
    for (produs, (min, max)) in lista {
        println!("{produs}: între {min:.2} și {max:.2} lei");
    }
}

Rezultat

lapte: între 7.90 și 9.20 lei
pâine: între 5.00 și 5.50 lei

Exemplul 3: Modificare fără inserare

Fără or_insert, o cheie lipsă rămâne lipsă: „pere” nu apare în dicționar.

use std::collections::HashMap;

fn main() {
    let mut stoc = HashMap::from([("mere", 10)]);
    stoc.entry("mere").and_modify(|n| *n -= 3);
    stoc.entry("pere").and_modify(|n| *n -= 3);
    println!("{:?}", stoc);
}

Rezultat

{"mere": 7}

Greșeli frecvente

  • Scrii and_modify după or_insert: or_insert returnează &mut V, nu Entry, deci nu se poate înlănțui. Ordinea corectă: întâi and_modify, apoi or_insert.
  • Te aștepți ca and_modify să ruleze și pentru valoarea tocmai inserată de or_insert. Nu rulează: o cheie nouă primește exact valoarea din or_insert.

Comenzi înrudite

  • 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.
  • Entry::or_insert: Dacă cheia lipsește, inserează valoarea dată; în ambele cazuri returnează o referință mutabilă la valoarea din dicționar.
  • HashMap::get_mut: Returnează o referință mutabilă la valoarea unei chei, ca s-o modifici pe loc; None dacă cheia nu există.
  • Entry::or_default: Dacă cheia lipsește, inserează valoarea implicită a tipului (0, vector gol, text gol) și returnează &mut V.