Accessing static variables using Rust

Sometimes when we are developing in rust, we miss some sort of "global variables" or "global instances" that we can access no matter from where in the code and simply know that it will always be the same position in Memory, that the heap will not de-allocate this memory and that it's reference will not be lost throughout the duration of the execution of the script.


However, achieving this in rust may not be ideal or straight forward. Which is why today we are quickly going to talk about OnceCell for Rust .

Once Cell - Init data Once

A "once cell" in Rust is a synchronization primitive that allows for the safe and efficient initialization of data exactly once. This can be particularly useful for scenarios where you need to initialize a static or global variable lazily, ensuring that the initialization logic is only executed once, even in the presence of concurrent access. The once_cell crate provides two main types: OnceCell and Lazy.

Key features and usage

OnceCell:

Purpose: It provides a way to initialize a value once and then access it multiple times without reinitializing.

Initialization: You can use the set method to initialize the value. This can only be done once; subsequent calls to set will fail. 

Access: Once initialized, the value can be accessed using the get method, which returns an Option<&T> (a reference to the value if it has been set).

use async_once_cell::OnceCell;
use rocket::local::asynchronous::Client;

use super::app::create_rocket_instance;
pub struct TestingRuntime {
pub client: Client,
}
pub trait Mock {
fn mock() -> Self;
}
impl TestingRuntime{
async fn new() -> Self {
let rocket = create_rocket_instance(true).await;
let client = Client::tracked(rocket).await.expect("valid rocket instance");
Self { client }
} pub async fn get() -> &'static TestingRuntime {
TEST_RUNTIME.get_or_init(async {
TestingRuntime::new().await
}).await
}
}

static TEST_RUNTIME: OnceCell = OnceCell::new();

In this previous example, we have a function called "create_rocket_instance()" that returns an object of type Rocket<build> (for those of you who are not familiar with it, Rocket would be the Rust equivalent of Express js. ).

 

During my testing, every time I launched a test,  I would be building this rocket instance , which would take a toll in test duration as well as code reusability and memory efficiency. Why did I need to create a new rocket instance every single time?

 

OnceCell() creates a reference in memory that you can then reuse over and over.  The method "get_or_init() will call for a variable and turn it into a static reference. Once the variable is created the first time (in this case, we are calling testingruntime::new() the variable is stored in thie reference, and whenever we call TestingRuntime::get(), it will either create the new variable, or grab the reference from the previous time it was called and return it. This way we are not creating one rocket instance (or any other data type instance) every single time we call a function and we also benefit from each one of these instances of being singleton.

Benefits

Thread-Safety: Both OnceCell and Lazy are designed to be used in multi-threaded contexts and ensure that the initialization logic is only executed once, even if multiple threads are attempting to access or initialize the value concurrently.

Performance: After the initial initialization, access to the value is very fast, as it does not involve any additional synchronization overhead.


Practical Use Cases:

Configuration Loading: Load configuration data from a file or environment variables only once and make it accessible throughout the application.

Singleton Pattern: Implement the singleton pattern where a single instance of a resource is shared across the application.

Cache Initialization: Initialize a cache or other expensive-to-create data structures lazily and share it across multiple threads.


In summary, once_cell in Rust provides powerful primitives for one-time initialization, ensuring thread safety and efficiency, which is essential for many real-world applications.