Question regarding once_cell and lazy_static

The doc of the crate once_cell mentions that the code below is equivalent to a lazy_static! {} block. Notice that a Mutex is used due to the fact that a static variable in Rust must be Sync (unless mut which is discouraged).

use std::{sync::Mutex, collections::HashMap};
use once_cell::sync::Lazy;

static GLOBAL_DATA: Lazy<Mutex<HashMap<i32, String>>> = Lazy::new(|| {
    let mut m = HashMap::new();
    m.insert(13, "Spica".to_string());
    m.insert(74, "Hoyten".to_string());
    Mutex::new(m)
});

fn main() {
    println!("{:?}", GLOBAL_DATA.lock().unwrap());
}

The below is an example from the doc of lazy_static.

use lazy_static::lazy_static;
use std::collections::HashMap;

lazy_static! {
    static ref HASHMAP: HashMap<u32, &'static str> = {
        let mut m = HashMap::new();
        m.insert(0, "foo");
        m.insert(1, "bar");
        m.insert(2, "baz");
        m
    };
}

fn main() {
    // First access to `HASHMAP` initializes it
    println!("The entry for `0` is \"{}\".", HASHMAP.get(&0).unwrap());

    // Any further access to `HASHMAP` just returns the computed value
    println!("The entry for `1` is \"{}\".", HASHMAP.get(&1).unwrap());
}

HASHMAP defined using the lazy_static macro is a static reference to a HashMap. How does lazy_static dodge the requirement of Sync for static variable here?

HashMaps are Sync without Mutex, too (provided their key and value types are). The useful thing the Mutex adds is mutability (after the initialization); without it, all you can do is read the hashmap.

In all these regards, lazy_statics and static once_cell::sync::Lazys should behave the same.

4 Likes

It's odd to me that the following code works

lazy_static! {
        static ref _2C_3S_8S_8D_TD: Vec<Card> = from_str("2c 3s 8s 8d Td").unwrap();
}

but the following code doesn't work.

use once_cell::unsync::Lazy;

static _2C_3S_8S_8D_TD: Lazy<Vec<Card>> = Lazy::new(|| {
    from_str("2c 3s 8s 8d Td").unwrap()
});

The compile says that a static shared variable must be sync. How does lazy_static get around of this?

You've used unsync::Lazy, which is explicitly not Sync.

1 Like

I changed use once_cell::unsync::Lazy; to use once_cell::sync::Lazy; and it indeed compiles now. However, I have another related question here. Does once_cell::sync::Lazy automatically make Vec<Card> Sync? Card does not implement Sync.

Could you share the definition of Card? Send and Sync are auto-traits, you almost always don't have to implement them manually.

2 Likes

The definition of Card is as below.

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Card {
    suit: Suit,

    rank: Rank,

    index: u8,

    id: u64,
}

where the definitions of Rank and Suit are as below.

#[derive(Debug, Copy, Clone, FromPrimitive, PartialEq, Eq, Hash)]
pub enum Rank {
    _2 = 2,
    _3,
    _4,
    _5,
    _6,
    _7,
    _8,
    _9,
    _T,
    _J,
    _Q,
    _K,
    _A,
}
#[derive(Debug, Copy, Clone, FromPrimitive, PartialEq, Eq, Hash)]
pub enum Suit {
    Diamonds,
    Clubs,
    Hearts,
    Spades,
    Jokers,
}

I didn't manually implement Sync for Card, Rank or Suit.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.