Ce face Entry::or_insert_with
or_insert_with(|| calcul) apelează closure-ul numai când cheia nu există. Este varianta potrivită când valoarea implicită costă: o alocare, citirea unui fișier, un calcul lung, sau când are efecte (un contor, un mesaj).
Ca și or_insert, returnează &mut V la valoarea din dicționar. Dacă valoarea depinde de cheie, or_insert_with_key(|k| ...) primește cheia ca parametru. Pentru valoarea implicită a tipului (0, Vec::new(), String::new()), cel mai scurt este or_default().
Parametri
| Parametru | Descriere |
|---|---|
default |
Closure fără parametri care produce valoarea (FnOnce() -> V). |
Ce returnează
&mut V.
Exemple
Exemplul 1: or_insert calculează mereu, or_insert_with doar la nevoie
Pentru trei apeluri cu două chei distincte, or_insert apelează funcția de 3 ori, iar or_insert_with de 2 ori.
use std::collections::HashMap;
fn valoare_initiala() -> u32 {
println!(" (calculez valoarea inițială)");
0
}
fn main() {
let chei = ["Ana", "Mihai", "Ana"];
println!("or_insert:");
let mut a: HashMap<&str, u32> = HashMap::new();
for k in chei {
*a.entry(k).or_insert(valoare_initiala()) += 1;
}
println!("or_insert_with:");
let mut b: HashMap<&str, u32> = HashMap::new();
for k in chei {
*b.entry(k).or_insert_with(valoare_initiala) += 1;
}
println!("Ana: {} și {}", a["Ana"], b["Ana"]);
}
Rezultat
or_insert:
(calculez valoarea inițială)
(calculez valoarea inițială)
(calculez valoarea inițială)
or_insert_with:
(calculez valoarea inițială)
(calculez valoarea inițială)
Ana: 2 și 2
Exemplul 2: Cache: fiecare număr este verificat o singură dată
Rezultatul lui este_prim este păstrat în dicționar. Cererile repetate nu mai calculează nimic.
use std::collections::HashMap;
fn este_prim(n: u64) -> bool {
n >= 2 && (2..).take_while(|d| d * d <= n).all(|d| n % d != 0)
}
fn main() {
let mut cache: HashMap<u64, bool> = HashMap::new();
let mut calcule = 0;
for n in [97, 100, 97, 7919, 100, 97] {
let prim = *cache.entry(n).or_insert_with(|| {
calcule += 1;
este_prim(n)
});
println!("{n}: {}", if prim { "prim" } else { "compus" });
}
println!("calcule făcute: {calcule} pentru 6 cereri");
}
Rezultat
97: prim
100: compus
97: prim
7919: prim
100: compus
97: prim
calcule făcute: 3 pentru 6 cereri
Exemplul 3: or_insert_with_key: valoarea depinde de cheie
use std::collections::HashMap;
fn main() {
let mut lungimi: HashMap<&str, usize> = HashMap::new();
for oras in ["Iași", "Cluj", "Iași", "Timișoara"] {
let n = lungimi.entry(oras).or_insert_with_key(|k| k.chars().count());
println!("{oras}: {n} litere");
}
}
Rezultat
Iași: 4 litere
Cluj: 4 litere
Iași: 4 litere
Timișoara: 9 litere
Greșeli frecvente
- Scrii
or_insert_with(Vec::new()): aceea este o valoare, nu un closure (expected an `FnOnce()` closure). Scrieor_insert_with(Vec::new)(fără paranteze) sauor_insert_with(|| Vec::new()); pentru acest caz,or_default(). - Folosești
or_insert_withpentru o constantă simplă (|| 0). Merge, daror_insert(0)sauor_default()sunt mai clare.
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_default: Dacă cheia lipsește, inserează valoarea implicită a tipului (0, vector gol, text gol) și returnează &mut V.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.Fn, FnMut și FnOnce: Cele trei trait-uri ale closure-urilor: Fn doar citește ce a capturat, FnMut modifică, FnOnce consumă și poate fi apelat o singură dată.