How to Idiomatically Use Global Variables in Rust

How to Idiomatically Use Global Variables in Rust – SitePointSkip to main contentFree JavaScript Book!Write powerful, clean and maintainable JavaScript.RRP $11.95 Declaring and using global variables in Rust can be tricky. Typically for this language, Rust ensures robustness by forcing us to be very explicit.
In this article, I’ll discuss the pitfalls the Rust compiler wants to save us from. Then I’ll show you the best solutions available for different scenarios.
Overview
There are many options for implementing global state in Rust. If you’re in a hurry, here’s a quick overview of my recommendations.

You can jump to specific sections of this article via the following links:
A Naive First Attempt at Using Global Variables in Rust
Let’s start with an example of how not to use global variables. Assume I want to store the starting time of the program in a global string. Later, I want to access the value from multiple threads.
A Rust beginner might be tempted to declare a global variable exactly like any other variable in Rust, using let. The full program could then look like this:
use chrono::Utc;

let START_TIME = Utc::now().to_string();

pub fn main() {
let thread_1 = std::thread::spawn(||{
println!(“Started {}, called thread 1 {}”, START_TIME.as_ref().unwrap(), Utc::now());
});
let thread_2 = std::thread::spawn(||{
println!(“Started {}, called thread 2 {}”, START_TIME.as_ref().unwrap(), Utc::now());
});

thread_1.join().unwrap();
thread_2.join().unwrap();
}

Try it for yourself on the playground!

This is invalid syntax for Rust. The let keyword can’t be used in the global scope. We can only use static or const. The latter declares a true constant, not a variable. Only static gives us a global variable.
The reasoning behind this is that let allocates a variable on the stack, at runtime. Note that this remains true when allocating on the heap, as in let t = Box::new();. In the generated machine code, there is still a pointer into the heap which gets stored on the stack.
Global variables are stored in the data segment of the program. They have a fixed address that doesn’t change during execution. Therefore, the code segment can include constant addresses and requires no space on the stack at all.
Okay, so we can understand why we need a different syntax. Rust, as a modern systems programming language, wants to be very explicit about memory management.
Let’s try again with static:
use chrono::Utc;

static START_TIME: String = Utc::now().to_string();

pub fn main() {

}

The compiler isn’t happy, yet:
error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
— > src/main.rs:3:24
|
3 | static start: String = Utc::now().to_string();
| ^^^^^^^^^^^^^^^^^^^^^^

Hm, so the initialization value of a static variable can’t be computed at runtime. Then maybe just let it be uninitialized?
use chrono::Utc;

static START_TIME;

pub fn main() {

}

This yields a new error:
Compiling playground v0.0.1 (/playground)
error: free static item without body
— > src/main.rs:21:1
|
3 | static START_TIME;
| ^^^^^^^^^^^^^^^^^-
| |
| help: provide a definition for the static: `= ;`

So that doesn’t work either! All static values must be fully initialized and valid before any user code runs.
If you’re coming over to Rust from another language, such as JavaScript or Python, this might seem unnecessarily restrictive. But any C++ guru can tell you stories about the static initialization order fiasco, which can lead to an undefined initialization order if we’re not careful.
For example, imagine something like this:
static A: u32 = foo();
static B: u32 = foo();
static C: u32 = A + B;

fn foo() – > u32 {
C + 1
}

fn main() {
println!(“A: {} B: {} C: {}”, A, B, C);
}

In this code snippet, there’s no safe initialization order, due to circular dependencies.
If it were C++, which doesn’t care about safety, the result would be A: 1 B: 1 C: 2. It zero-initializes before any code runs and then the order is defined from top-to-bottom within each compilation unit.
At least it’s defined what the result is. However, the “fiasco” starts when the static variables are from different .cpp files, and therefore different compilation units. Then the order is undefined and usually depends on the order of the files in the compilation command line.
In Rust, zero-initializing is not a thing. After all, zero is an invalid value for many types, such as Box. Furthermore, in Rust, we don’t accept weird ordering issues. As long as we stay away from unsafe, the compiler should only allow us to write sane code. And that’s why the compiler prevents us from using straightforward runtime initialization.
But can I circumvent initialization by using None, the equivalent of a null-pointer? At least this is all in accordance with the Rust type system. Surely I can just move the initialization to the top of the main function, right?
static mut START_TIME: Option = None;

pub fn main() {
START_TIME = Some(Utc::now().to_string());

}

Ah, well, the error we get is…
error[E0133]: use of mutable static is unsafe and requires unsafe function or block
— > src/main.rs:24:5
|
6 | START_TIME = Some(Utc::now().to_string());
| ^^^^^^^^^^ use of mutable static
|
= note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior

At this point, I could wrap it in an unsafe{…} block and it would work. Sometimes, this is a valid strategy. Maybe to test if the remainder of the code works as expected. But it’s not the idiomatic solution I want to show you. So let’s explore solutions that are guaranteed to be safe by the compiler.
Refactor the Example
You may already have noticed that this example doesn’t require global variables at all. And more often than not, if we can think of a solution without global variables, we should avoid them.
The idea here is to put the declaration inside the main function:
pub fn main() {
let start_time = Utc::now().to_string();
let thread_1 = std::thread::spawn(||{
println!(“Started {}, called thread 1 {}”, &start_time, Utc::now());
});
let thread_2 = std::thread::spawn(||{
println!(“Started {}, called thread 2 {}”, &start_time, Utc::now());
});

thread_1.join().unwrap();
thread_2.join().unwrap();
}

The only problem is the borrow-checker:
error[E0373]: closure may outlive the current function, but it borrows `start_time`, which is owned by the current function
— > src/main.rs:42:39
|
42 | let thread_1 = std::thread::spawn(||{
| ^^ may outlive borrowed value `start_time`
43 | println!(“Started {}, called thread 1 {}”, &start_time, Utc::now());
| ———- `start_time` is borrowed here
|
note: function requires argument type to outlive `’static`

This error is not exactly obvious. The compiler is telling us that the spawned thread may live longer than the value start_time, which lives in the stack frame of the main function.
Technically, we can see that this is impossible. The threads are joined, thus the main thread won’t exit before the child threads have finished.
But the compiler isn’t smart enough to figure out this particular case. In general, when a new thread is spawned, the provided closure can only borrow items with a static lifetime. In other words, the borrowed values must be alive for the full program lifetime.
For anyone just learning about Rust, this could be the point where you want to reach out to global variables. But there are at least two solutions that are much easier than that. The simplest is to clone the string value and then move ownership of the strings into the closures. Of course, that requires an extra allocation and some extra memory. But in this case, it’s just a short string and nothing performance-critical.
But what if it was a much larger object to share? If you don’t want to clone it, wrap it behind a reference-counted smart pointer. Rc is the single-threaded reference-counted type. Arc is the atomic version that can safely share values between threads.
So, to satisfy the compiler, we can use Arc as follows:

pub fn main() {
let start_time = Arc::new(Utc::now().to_string());

let cloned_start_time = start_time.clone();
let thread_1 = std::thread::spawn(move ||{
println!(“Started {}, called thread 1 {}”, cloned_start_time, Utc::now());
});
let thread_2 = std::thread::spawn(move ||{
println!(“Started {}, called thread 2 {}”, start_time, Utc::now());
});

thread_1.join().unwrap();
thread_2.join().unwrap();
}

Try it for yourself on the playground!

This has been a quick rundown on how to share state between threads while avoiding global variables. Beyond what I’ve shown you so far, you might also need interior mutability to modify the shared state. Full coverage of interior mutability is outside the scope of this article. But in this particular example, I would pick Arc to add thread-safe interior mutability to start_time.
When the Global Variable Value Is Known at Compile Time
In my experience, the most common use cases for global state are not variables but constants. In Rust, they come in two flavors:
Constant values, defined with const. These are inlined by the compiler. Interior mutability is never allowed.
Static variables, defined with static. They receive a fixed space in the data segment. Interior mutability is possible.
Both of them can be initialized with compile-time constants. These could be simple values, such as 42 or “hello world”. Or it could be an expression involving several other compile-time constants and functions marked as const. As long as we avoid circular dependencies. (You can find more details on constant expressions in The Rust Reference.)
use std::sync::atomic::AtomicU64;
use std::sync::{Arc,Mutex};

static COUNTER: AtomicU64 = AtomicU64::new(TI_BYTE);

const GI_BYTE: u64 = 1024 * 1024 * 1024;
const TI_BYTE: u64 = 1024 * GI_BYTE;

Usually, const is the better choice — unless you need interior mutability, or you specifically want to avoid inlining.
Should you require interior mutability, there are several options. For most primitives, there’s a corresponding atomic variant available in std::sync::atomic. They provide a clean API to load, store, and update values atomically.
In the absence of atomics, the usual choice is a lock. Rust’s standard library offers a read-write lock (RwLock) and a mutual exclusion lock (Mutex).
However, if you need to calculate the value at runtime, or need heap-allocation, then const and static are of no help.
Single-threaded Global Variables in Rust with Runtime Initialization
Most applications I write only have a single thread. In that case, a locking mechanism isn’t necessary.
However, we shouldn’t use static mut directly and wrap accesses in unsafe, just because there’s only one thread. This way, we could end up with serious memory corruption.
For example, borrowing unsafely from the global variable could give us multiple mutable references simultaneously. Then we could use one of them to iterate over a vector and another to remove values from the same vector. The iterator could then go beyond the valid memory boundary, a potential crash that safe Rust would have prevented.
But the standard library has a way to “globally” store values for safe access within a single thread. I’m talking about thread locals. In the presence of many threads, each thread gets an independent copy of the variable. But in our case, with a single thread, there’s only one copy.
Thread locals are created with the thread_local! macro. Accessing them requires the use of a closure, as shown in the following example:
use chrono::Utc;

thread_local!(static GLOBAL_DATA: String = Utc::now().to_string());

fn main() {
GLOBAL_DATA.with(|text| {
println!(“{}”, *text);
});
}

It’s not the simplest of all solutions. But it allows us to perform arbitrary initialization code, which will run just in time when the first access to the value occurs.
Thread-locals are really good when it comes to interior mutability. Unlike all the other solutions, it doesn’t require Sync. This allows using RefCell for interior mutability, which avoids the locking overhead of Mutex.
The absolute performance of thread-locals is highly dependent on the platform. But I did some quick tests on my own PC comparing it to interior mutability relying on locks and found it to be 10x faster. I don’t expect the result to be flipped on any platform, but make sure to run your own benchmarks if you deeply care about performance.
Here’s an example of how to use RefCell for interior mutability:
thread_local!(static GLOBAL_DATA: RefCell = RefCell::new(Utc::now().to_string()));

fn main() {
GLOBAL_DATA.with(|text| {
println!(“Global string is {}”, *text.borrow());
});

GLOBAL_DATA.with(|text| {
*text.borrow_mut() = Utc::now().to_string();
});

GLOBAL_DATA.with(|text| {
println!(“Global string is {}”, *text.borrow());
});
}

Try it for yourself on the playground!

As a side note, even though threads in WebAssembly are different from threads on an x86_64 platform, this pattern with thread_local! + RefCell is also applicable when compiling Rust to run in the browser. Using an approach that’s safe for multi-threaded code would be overkill in that case. (If the idea of running Rust inside the browser is new to you, feel free to read a previous article I wrote called “Rust Tutorial: An Introduction to Rust for JavaScript Devs”.)
One caveat about thread-locals is that their implementation depends on the platform. Usually, this is nothing you’d notice, but be aware that the drop-semantics are platform-dependent because of that.
All that said, the solutions for multi-threaded globals obviously also work for the single-threaded cases. And without interior mutability, those seem to be just as fast as thread-locals.
So let’s have a look at that next.
Multi-threaded Global Variables with Runtime Initialization
The standard library currently has no great solution for safe global variables with runtime initialization. However, using std::sync::Once, it’s possible to build something that uses unsafe safely, if you know what you’re doing.
The example in the official documentation is a good starting point. Should you also need interior mutability, you’d have to combine that approach with a read-write lock or a mutex. Here’s how that might look:
static mut STD_ONCE_COUNTER: Option = None;
static INIT: Once = Once::new();

fn global_string

19 Best PHP Event Calendar and Booking Scripts… and 3 Free Options

In this article, I’ll review 19 of the best PHP calendar scripts. Whether you need an appointment booking script, an event calendar script, a web calendar script, or a PHP calendar reservation system, you’ll find something on this list. There are lots of reasons you might need a PHP calendar script. If you’re a service…

7 Tips to Design and Sell Merchandise Online

Have you ever started looking for a new shirt or gift and thought, “I could design something so much better than what I see here?” With a little planning and some good ideas, you can design and sell merchandise online featuring your artwork. It’s gotten easier over time with so many marketplaces available as well…

I made a YouTube playlist of me building a real website from scratch of one of my clients and explain everything I do and why to help beginners think like a developer. This is for anyone wishing they could job shadow someone as they worked.

If you recognize this post that’s because it was on the front page of this sub for a while before it was removed by the mods for not being posted on showoff Saturday and violated the rules. I’ve had a lot of people message me asking where the link went so I am reposting so everyone can have access to it and find it on the sub when they want to. Hope that’s ok with everyone. It seemed to be really helpful to everyone and was well received so I wanted to make sure it was available where you all can find it.I also added three new videos to show how to optimize your website page speeds from 50’s-60’s to 97+ score, how to connect to a domain with netlify via GitHub, and how to set up google analytics and search console.So now this playlist goes over the entire process from start to Finish.Here’s the text of the precious post:For anyone wanting to learn web development – Here’s the playlist:https://youtube.com/playlist?list=PLMPdeA59PPg2Cbd3cul0wFOY2KCbb4IIDLots of good stuff in this one to learn how to make a mobile first and responsive website with no frameworks, just html and css.I go over all my decisions and explain why I do things a certain way. I did not plan this video out – I run into problems and I talk through them. I left everything on these videos so you can learn how to think through problems yourself when you get started building your own websites.So I explain everything I do and why I make the decisions I make so others can see HOW to think like a front end developer.I also go over how to transfer a desktop design to a mobile design and how to decide what to keep and what to change. It’s not always easy to figure out how to make a desktop design into a mobile one, but that’s what I do here and hopefully it helps!If you liked that, here’s the series I did last week for a MUCH more complicated and very modern design with a ton of useful css tricks and everything I mention earlier:https://youtube.com/playlist?list=PLMPdeA59PPg2sLFYU3f-vITZgOWVSCZ6eEDIT:Here’s a live demo link to the site I made in the video all complete if y’all wanted to see it:https://forcedevolution.netlify.appStill not finalized yet. Gotta write content and work with my other developer to integrate my code into Shopify and insert the store where it needs to be.Hopefully this is helpful. It’s not exactly a tutorial, more like an implementation of what tutorials try to teach you. So if you’re tired of tutorial hell this should be refreshing. Feel free to ask any questions!NEWLY ADDED VIDEOS:Optimizing your website for 97+ page speed: https://youtu.be/XHVbqmyCSeQConnecting to a custom domain: https://youtu.be/mT9vX69YC5AAnd setting up a analytics and search console: https://youtu.be/kFu0V9dSqQk

Core Web Vitals: New Ranking Factor and How to Improve It

In recent years, Google and other similar companies have become increasingly concerned with the user experience (UX); that’s why the UX always pops up in algorithm updates. With 88 percent of online users turning away from websites after one bad experience, it goes to show that UX is vital.  Google, being the pioneer, decided to…

Three Insights I Gained While Researching Vue.js Accessibility

JavaScript frameworks like React, Angular and Vue have a very bad reputation when it comes to web accessibility. But is this due to inherent technical limitations or insurmountable problems of those tools? I think not. During the research phase of my book, “Accessible Vue,” I gained three insights regarding web app accessibility in general and…

Best Classified Plugins for WordPress

Do you want to create a classified ads website to publish listings and advertisements? Do you want to add a classified section on your blog or magazine?   Building a classified website from scratch is a massive undertaking that will cost you a lot of time and money. Fortunately, classified plugins exist with all the…