Sling Academy
Home/Rust/Rust - Creating a global or static HashMap using lazy_static or once_cell

Rust - Creating a global or static HashMap using lazy_static or once_cell

Last updated: January 04, 2025

In many programming scenarios, particularly in Rust, there arises a requirement to create a global or static HashMap. This task may seem daunting due to the restrictions Rust imposes to ensure safe data handling. Two common crates, lazy_static and once_cell, facilitate lazy initialization, allowing us to create such structures safely and efficiently. In this article, we'll delve into how these crates work and provide examples of using each to create a global or static HashMap.

Lazy initialization is the technique of delaying the creation of an object, calculation, or another expensive process until immediately before it's first needed. This approach optimizes performance and memory usage significantly, especially for global data constructs that might not always be required during the program's runtime.

Using lazy_static Crate

The lazy_static crate allows the creation of global variables that require initialization, such as HashMap. The essence of lazy_static is that the variables are only initialized when they are accessed for the first time. This is particularly useful for data structures like HashMap which might impose significant costs if initialized at compile time.

// Add lazy_static to your dependencies in Cargo.toml:
// lazy_static = "1.4"

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

lazy_static! {
    static ref GLOBAL_MAP: HashMap<&'static str, i32> = {
        let mut m = HashMap::new();
        m.insert("foo", 42);
        m.insert("bar", 33);
        m
    };
}

fn main() {
    println!("Value for 'foo': {}", GLOBAL_MAP["foo"]);
}

In this example, a HashMap is initialized the first time GLOBAL_MAP is accessed in the main function. lazy_static! ensures thread-safe lazy initialization.

Using once_cell Crate

once_cell is another crate that simplifies the creation and use of lazy initialized global static variables. Unlike lazy_static, once_cell is part of the std library as of Rust 1.56, making it a native choice for many developers.

// Add once_cell to your dependencies in Cargo.toml:
// once_cell = "1.5"

use once_cell::sync::Lazy;
use std::collections::HashMap;

fn main() {
    static GLOBAL_MAP: Lazy<HashMap<String, i32>> = Lazy::new(|| {
        let mut m = HashMap::new();
        m.insert("foo".to_string(), 42);
        m.insert("bar".to_string(), 33);
        m
    });

    println!("Value for 'foo': {}", GLOBAL_MAP["foo"]);
}

The Lazy type provides a straightforward way to leverage lazy initialization over static variables. With once_cell, it achieves safe, concurrent initialization just like lazy_static!, but is generally favored because it blends into the standard toolset of Rust if you choose its native version.

Choosing the Right Tool

Deciding whether to use lazy_static or once_cell depends largely on your project's needs and your preferred syntax style. lazy_static syntax, though slightly more verbose, has been around longer and is well-documented. On the other hand, once_cell is newer but integrates better with modern Rust as it moves towards inclusion in future releases.

Both crates are efficient and serve the purpose admirably, maintaining thread safety by locking the initialization during the first access and all subsequent reads taking place without locks.

Conclusion

Creating a globally accessible HashMap in Rust can be managed effectively using lazy initialization patterns provided by libraries like lazy_static and once_cell. These tools offer flexibility, safety, and efficiency, ensuring that your applications remain performant and straightforward to manage. As Rust continues evolving, leveraging these tools not only meets today's needs but also future proofs your codebase against evolving practices.

Ultimately, the choice of whether to use lazy_static or once_cell will depend on you and your team's coding style, legacy support requirements, and the target rust version compatibility.

Next Article: Rust: Serializing and deserializing HashMaps and Vectors with Serde

Previous Article: Rust - Comparing and contrasting HashMap with BTreeMap for sorted data

Series: Collections in Rust

Rust

You May Also Like

  • E0557 in Rust: Feature Has Been Removed or Is Unavailable in the Stable Channel
  • Network Protocol Handling Concurrency in Rust with async/await
  • Using the anyhow and thiserror Crates for Better Rust Error Tests
  • Rust - Investigating partial moves when pattern matching on vector or HashMap elements
  • Rust - Handling nested or hierarchical HashMaps for complex data relationships
  • Rust - Combining multiple HashMaps by merging keys and values
  • Composing Functionality in Rust Through Multiple Trait Bounds
  • E0437 in Rust: Unexpected `#` in macro invocation or attribute
  • Integrating I/O and Networking in Rust’s Async Concurrency
  • E0178 in Rust: Conflicting implementations of the same trait for a type
  • Utilizing a Reactor Pattern in Rust for Event-Driven Architectures
  • Parallelizing CPU-Intensive Work with Rust’s rayon Crate
  • Managing WebSocket Connections in Rust for Real-Time Apps
  • Downloading Files in Rust via HTTP for CLI Tools
  • Mocking Network Calls in Rust Tests with the surf or reqwest Crates
  • Rust - Designing advanced concurrency abstractions using generic channels or locks
  • Managing code expansion in debug builds with heavy usage of generics in Rust
  • Implementing parse-from-string logic for generic numeric types in Rust
  • Rust.- Refining trait bounds at implementation time for more specialized behavior