Ce face OnceLock
OnceLock<T> începe gol. Prima chemare a lui get_or_init(f) rulează funcția, păstrează rezultatul și returnează &T. Chemările următoare returnează direct valoarea deja salvată, fără să mai ruleze funcția. Dacă mai multe fire cheamă în același timp, doar unul rulează funcția, iar celelalte așteaptă.
Pentru că new() este const, un OnceLock poate fi static. Așa se fac valorile globale care nu pot fi calculate la compilare. Mai există get() (citește fără să inițializeze, dă Option) și set() (pune valoarea dacă celula e goală).
Parametri
| Parametru | Descriere |
|---|---|
f |
Funcția care creează valoarea; rulează cel mult o dată. |
Ce returnează
get_or_init: &T, referință la valoarea salvată.
Exemple
Exemplul 1: Configurare globală inițializată o dată
Mesajul „încarc configurarea” apare o singură dată, deși funcția config() este apelată de trei ori.
use std::sync::OnceLock;
struct Config {
oras: String,
tva: f64,
}
static CONFIG: OnceLock<Config> = OnceLock::new();
fn config() -> &'static Config {
CONFIG.get_or_init(|| {
println!("(încarc configurarea)");
Config { oras: String::from("Iași"), tva: 0.19 }
})
}
fn main() {
println!("oraș: {}", config().oras);
println!("TVA: {}", config().tva);
println!("preț cu TVA: {:.2} lei", 100.0 * (1.0 + config().tva));
}
Rezultat
(încarc configurarea)
oraș: Iași
TVA: 0.19
preț cu TVA: 119.00 lei
Exemplul 2: Mai multe fire, o singură inițializare
use std::sync::OnceLock;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
static APELURI: AtomicUsize = AtomicUsize::new(0);
static TABEL: OnceLock<Vec<u64>> = OnceLock::new();
fn patrate() -> &'static Vec<u64> {
TABEL.get_or_init(|| {
APELURI.fetch_add(1, Ordering::SeqCst);
(0..10).map(|x| x * x).collect()
})
}
fn main() {
println!("înainte: {:?}", TABEL.get());
thread::scope(|s| {
for _ in 0..4 {
s.spawn(|| patrate().len());
}
});
println!("pătrate: {:?}", patrate());
println!("inițializări: {}", APELURI.load(Ordering::SeqCst));
}
Rezultat
înainte: None
pătrate: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
inițializări: 1
Greșeli frecvente
- Folosești
static mutpentru o valoare globală: accesul esteunsafeși, din ediția 2024, referințele lastatic mutsunt eroare. FoloseșteOnceLocksauLazyLock. - Apelezi din funcția de inițializare tot
get_or_initpe aceeași celulă: firul se blochează (sau intră în panică).
Comenzi înrudite
LazyLock: Valoare calculată automat la prima folosire, sigur între fire; se folosește ca static global.Mutex: Protejează o valoare folosită din mai multe fire: lock() așteaptă până când firul curent este singurul cu acces.Option::get_or_insert_with: Dacă opțiunea este None, pune în ea valoarea calculată de funcție; apoi returnează o referință mutabilă la valoare.Box::leak: Renunță la un Box fără să elibereze memoria și dă o referință valabilă până la finalul programului (&’static).