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
LazyLock
ensures that theT
inside can be initialized safely in different threads. WhetherT
is concurrency-safe or not is not a concern it addresses. - I tried to use the
cfg_version
feature to ensure that Rust versions less than1.80
can still uselazy_static
when 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.