LazyLock în Rust

Valoare calculată automat la prima folosire, sigur între fire; se folosește ca static global.

Sintaxă
use std::sync::LazyLock;

static NUME: LazyLock<T> = LazyLock::new(|| valoare);

const fn new(f: F) -> LazyLock<T, F>

Ce face LazyLock

LazyLock::new(f) ține funcția f și o rulează abia la prima accesare a valorii. Spre deosebire de OnceLock, nu mai chemi get_or_init: folosești statica direct, ca pe valoarea însăși (MAP.get("x"), *NUMAR), iar inițializarea se face singură.

Este înlocuitorul standard al crate-ului lazy_static și al lui once_cell::sync::Lazy. Pentru o variantă fără sincronizare, folosită într-un singur fir, există std::cell::LazyCell.

Parametri

Parametru Descriere
f Funcția (de obicei un closure fără captură) care creează valoarea.

Ce returnează

Un LazyLock<T> care se folosește ca &T.

Exemple

Exemplul 1: Tabel global creat la prima folosire

Mesajul de inițializare apare abia la prima căutare, nu la pornirea programului.

use std::collections::BTreeMap;
use std::sync::LazyLock;

static JUDETE: LazyLock<BTreeMap<&str, &str>> = LazyLock::new(|| {
    println!("(construiesc tabelul de județe)");
    BTreeMap::from([("IS", "Iași"), ("CJ", "Cluj"), ("BV", "Brașov")])
});

fn main() {
    println!("pornesc programul");
    println!("IS = {:?}", JUDETE.get("IS"));
    println!("CJ = {:?}", JUDETE.get("CJ"));
    println!("XX = {:?}", JUDETE.get("XX"));
    println!("{} județe", JUDETE.len());
}

Rezultat

pornesc programul
(construiesc tabelul de județe)
IS = Some("Iași")
CJ = Some("Cluj")
XX = None
3 județe

Exemplul 2: O valoare calculată, folosită din mai multe fire

use std::sync::LazyLock;
use std::thread;

static PRIME: LazyLock<Vec<u32>> = LazyLock::new(|| {
    (2..50).filter(|n| (2..*n).all(|d| n % d != 0)).collect()
});

fn main() {
    let numere = [7, 21, 47];
    let raspunsuri: Vec<String> = thread::scope(|s| {
        let fire: Vec<_> = numere
            .iter()
            .map(|n| s.spawn(move || format!("{n} este prim: {}", PRIME.contains(n))))
            .collect();
        fire.into_iter().map(|f| f.join().unwrap()).collect()
    });
    for r in raspunsuri {
        println!("{r}");
    }
    println!("prime sub 50: {}", PRIME.len());
}

Rezultat

7 este prim: true
21 este prim: false
47 este prim: true
prime sub 50: 15

Greșeli frecvente

  • Te aștepți ca valoarea să fie creată la pornire: este creată abia la prima accesare. Dacă vrei verificarea erorilor la pornire, accesează statica explicit la începutul lui main.
  • Funcția de inițializare intră în panică: LazyLock devine otrăvit, iar orice accesare ulterioară intră și ea în panică.

Comenzi înrudite

  • OnceLock: Celulă care primește o valoare o singură dată, sigur între fire; ideală pentru valori globale calculate la prima folosire.
  • Mutex: Protejează o valoare folosită din mai multe fire: lock() așteaptă până când firul curent este singurul cu acces.
  • Box::leak: Renunță la un Box fără să elibereze memoria și dă o referință valabilă până la finalul programului (&’static).