Re-write from lazy_static to LazyLock.
From Rust 1.80, LazyLock is now part of the stable channel. To be honest, it is the first time I realized the Rust team was trying to stabilize the lazy_static feature. This is great news because I have been using lazy_static in some of my projects, and I am eager to rewrite them using LazyLock (LazyCell).
What did I do with lazy_static?
I use lazy_static similarly to how other languages use global values. Sometimes, I just want to keep a global variable. I don’t understand the implementation details of lazy_static; I just use it like (defparameter) in Lisp.
Start to rewrite it
The first project I am thinking of rewriting is my code-it-later-rs. I use lazy_static to generate three global static tables.
The change is:
@@ -25,19 +24,19 @@ const DICT: &'static str = r#"
}
"#;
-lazy_static! {
- /// The table including all languages filetype and its comment symbols
- static ref TABLE: Mutex<HashMap<String, Vec<String>>> =
- Mutex::new(serde_json::from_str(DICT).unwrap());
+static TABLE: LazyLock<Mutex<HashMap<String, Vec<String>>>> =
+ LazyLock::new(|| Mutex::new(serde_json::from_str(DICT).unwrap()));
- /// Regex table, like "rs" => "(//+):=\s+(.*)"
- pub static ref REGEX_TABLE: Mutex<HashMap<String, Regex>> = Mutex::new({
+pub static REGEX_TABLE: LazyLock<Mutex<HashMap<String, Regex>>> = LazyLock::new(|| {
+ Mutex::new({
let a = TABLE.lock().unwrap();
- a.iter().map(|(k, v)| (k.clone(), Regex::new(&make_regex(v)).unwrap())).collect()
- });
+ a.iter()
+ .map(|(k, v)| (k.clone(), Regex::new(&make_regex(v)).unwrap()))
+ .collect()
+ })
+});
- pub static ref KEYWORDS_REGEX: Mutex<Option<Regex>> = Mutex::new(None);
-}
+pub static KEYWORDS_REGEX: LazyLock<Mutex<Option<Regex>>> = LazyLock::new(|| Mutex::new(None));
It is pretty straightforward. At the beginning, I tried to rewrite it like this:
static TABLE: LazyLock<Mutex<HashMap<String, Vec<String>>>> =
LazyLock::new(|| serde_json::from_str(DICT).unwrap());
because I thought it might have a lock inside. Then I figured out that LazyLock doesn’t have any Sync/Send constraints at all. It also doesn’t have the as_mut method like Mutex. So I needed to add the Mutex, just as I did with lazy_static.
Furthermore, because LazyLock<T> only implements the Deref trait and not Send/Sync, the concurrency safety guarantee is left to T itself. This means T must implement Sync.
According to the Sync documentation:
&T is Send if and only if T is Sync.
So, I think the LazyLock just implementing Deref is a good choice, making things simple.
Wrap up
- It appears that
LazyLockensures that theTinside can be initialized safely in different threads. WhetherTis concurrency-safe or not is not a concern it addresses. - I tried to use the
cfg_versionfeature to ensure that Rust versions less than1.80can still uselazy_staticwhen installing. However, it looks like this feature is still nightly and not included in the stable standard library yet. I might change it in the future.