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)]));
- Is this right way to move from
lazy_static
tostd
? - Is there a better way to implement constant
HashMaps
in general? - 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)]));
- Is this right way to move from
lazy_static
tostd
? - Is there a better way to implement constant
HashMaps
in general? - 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
|
2 Answers
Reset to default 2Why 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)
}
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 onlazy_static
in new code. – user4815162342 Commented Feb 6 at 8:58