最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

rust - How should one migrate from lazy_static to std LazyLockLazyCell - Stack Overflow

programmeradmin1浏览0评论

I created a library that uses lazy_static to create a static HashMap that other parts of the code can reference to look up values like this:

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


lazy_static! {
    pub static ref MAP: HashMap<&'static str, u8> =
        HashMap::from([("foo", 1), ("bar", 2), ("baz", 3), ("quxx", 99),]);
}

pub fn somefunction() {
    let foo = MAP.get("foo");
    // Do stuff with foo value
}

The idea is that MAP is accessible from anywhere that can import it but it is only ever initialized once and only one copy of it is ever made. I understand this pattern is a common way to implement constant hashmaps/lookup tables.

Now that std::cell::LazyCell and it's threadsafe cousin std::sync::LazyLock have been stabilized in Rust 1.80.0, I've been lead to believe these can replace lazy_static for the same purpose.

I managed to get things working with LazyLock like this:

static LLMAP: LazyLock<HashMap<&str, u16>> =
    LazyLock::new(|| HashMap::from([("foo", 1), ("bar", 69), ("baz", 420)]));
  1. Is this right way to move from lazy_static to std?
  2. Is there a better way to implement constant HashMaps in general?
  3. Why does this same thing not work with LazyCell which complains with the following?
error[E0277]: `UnsafeCell<cell::lazy::State<HashMap<&'static str, u16>, fn() -> HashMap<&'static str, u16>>>` cannot be shared between threads safely
  --> src/lib.rs:15:15
   |
15 | static LCMAP: LazyCell<HashMap<&str, u16>> =
   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<cell::lazy::State<HashMap<&'static str, u16>, fn() -> HashMap<&'static str, u16>>>` cannot be shared between threads safely
   |
   = help: within `LazyCell<HashMap<&'static str, u16>>`, the trait `Sync` is not implemented for `UnsafeCell<cell::lazy::State<HashMap<&'static str, u16>, fn() -> HashMap<&'static str, u16>>>`, which is required by `LazyCell<HashMap<&'static str, u16>>: Sync`
note: required because it appears within the type `LazyCell<HashMap<&'static str, u16>>`
  --> /home/tom/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/cell/lazy.rs:37:12
   |
37 | pub struct LazyCell<T, F = fn() -> T> {
   |            ^^^^^^^^
   = note: shared static variables must have a type that implements `Sync`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `lazy` (lib) due to 1 previous error

I created a library that uses lazy_static to create a static HashMap that other parts of the code can reference to look up values like this:

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


lazy_static! {
    pub static ref MAP: HashMap<&'static str, u8> =
        HashMap::from([("foo", 1), ("bar", 2), ("baz", 3), ("quxx", 99),]);
}

pub fn somefunction() {
    let foo = MAP.get("foo");
    // Do stuff with foo value
}

The idea is that MAP is accessible from anywhere that can import it but it is only ever initialized once and only one copy of it is ever made. I understand this pattern is a common way to implement constant hashmaps/lookup tables.

Now that std::cell::LazyCell and it's threadsafe cousin std::sync::LazyLock have been stabilized in Rust 1.80.0, I've been lead to believe these can replace lazy_static for the same purpose.

I managed to get things working with LazyLock like this:

static LLMAP: LazyLock<HashMap<&str, u16>> =
    LazyLock::new(|| HashMap::from([("foo", 1), ("bar", 69), ("baz", 420)]));
  1. Is this right way to move from lazy_static to std?
  2. Is there a better way to implement constant HashMaps in general?
  3. Why does this same thing not work with LazyCell which complains with the following?
error[E0277]: `UnsafeCell<cell::lazy::State<HashMap<&'static str, u16>, fn() -> HashMap<&'static str, u16>>>` cannot be shared between threads safely
  --> src/lib.rs:15:15
   |
15 | static LCMAP: LazyCell<HashMap<&str, u16>> =
   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<cell::lazy::State<HashMap<&'static str, u16>, fn() -> HashMap<&'static str, u16>>>` cannot be shared between threads safely
   |
   = help: within `LazyCell<HashMap<&'static str, u16>>`, the trait `Sync` is not implemented for `UnsafeCell<cell::lazy::State<HashMap<&'static str, u16>, fn() -> HashMap<&'static str, u16>>>`, which is required by `LazyCell<HashMap<&'static str, u16>>: Sync`
note: required because it appears within the type `LazyCell<HashMap<&'static str, u16>>`
  --> /home/tom/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/cell/lazy.rs:37:12
   |
37 | pub struct LazyCell<T, F = fn() -> T> {
   |            ^^^^^^^^
   = note: shared static variables must have a type that implements `Sync`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `lazy` (lib) due to 1 previous error
Share Improve this question asked Feb 6 at 3:05 RBF06RBF06 2,4192 gold badges24 silver badges21 bronze badges 2
  • Please focus on one question at a time – kmdreko Commented Feb 6 at 3:32
  • Yes, your approach to migrating from lazy_static is exactly right. It would be nice if this were documented in that crate itself - its docs currently don't even mention that mostly equivalent functionality is now available in std and that there is no need to depend on lazy_static in new code. – user4815162342 Commented Feb 6 at 8:58
Add a comment  | 

2 Answers 2

Reset to default 2

Why does this same thing not work with LazyCell which complains with the following?

To expand on the other answer: like the rest of cell, LazyCell is not thread-safe.

And rust says anything static must be thread-safe (or unsafe), as it can be accessed from multiple threads by virtue of being a global and thus shared by the entire program by definition.

Is there a better way to implement constant HashMaps in general?

Depending on your actual production requirements, https://crates.io/crates/phf can be a good fit, or just a very thin layer over a static slice: although lookup in a slice is O(n) (or O(log n) if bisection), it has a very small constant factor whereas a hashmap has a very high constant factor. So for small collection sizes, a linear search will generally be much faster than a hash.

I think a PHF can be faster than a linear search even at small sizes, but I'll admit I have very little experience with them, or with the PHF crate, so you'd have to bench them (and hashmap), and weigh the other tradeoffs.

static

A static item is similar to a constant, except that it represents a precise memory location in the program. All references to the static refer to the same memory location.

That means static items must impl Sync, and LazyCell doesn't.

Like how you cannot pass Rc between thread, but you can with Arc.

Handling HashMap

if you ever only use get in the hashmap, you can put it inside a function, it will still be static just not expose to the namespace.

pub fn get_int_by_key(k: &str) -> Option<&i32> {
    static LLMAP: LazyLock<HashMap<&'static str, i32>> =
        LazyLock::new(|| HashMap::from([("foo", 1), ("bar", 69), ("baz", 420)]));
    LLMAP.get(k)
}
发布评论

评论列表(0)

  1. 暂无评论