Hello, tech enthusiasts! We at LifenLearn are here to delve into the latest enhancements that the Rust 1.70.0 release has brought into the limelight. This update comes with the stabilization of two noteworthy types, OnceCell and OnceLock, aimed at one-time initialization of shared data. Let’s examine these new types, their potential use-cases, and how they can make your Rust programming more efficient.
A Closer Look at OnceCell and OnceLock
The advent of Rust 1.70.0 has paved the way for the stabilization of OnceCell and OnceLock. These two types are perfectly suited to scenarios where immediate construction of data is neither desirable nor feasible, such as non-const data in global variables.
These types are unique in that they allow initialization to happen exactly once and prevent re-initialization, making them ideal for use-cases where expensive operations need to be performed only once. This design is very much in line with Rust’s overall philosophy of explicitness and control over system resources.
Let’s consider two examples that demonstrate the use of OnceLock:
In the first code snippet:
use std::sync::OnceLock;
static WINNER: OnceLock<&str> = OnceLock::new();
fn main() {
let winner = std::thread::scope(|s| {
s.spawn(|| WINNER.set("Alice"));
std::thread::yield_now(); // give them a chance...
WINNER.get_or_init(|| "Bob")
});
println!("{} wins!", winner);
}
Here, a OnceLock global variable WINNER is created, which is intended to hold a static string (&str). Within a scoped thread created using std::thread::scope, a new thread is spawned that attempts to set the WINNER to “Alice”. The main thread yields, allowing the spawned thread a chance to run. If WINNER remains uninitialized, it gets set to “Bob”.
And in the second code snippet:
use std::sync::OnceLock;
use crossbeam::scope;
static WINNER: OnceLock<String> = OnceLock::new();
fn main() {
scope(|s| {
s.spawn(|_| {
let _ = WINNER.set("Alice".to_string());
});
std::thread::yield_now(); // give them a chance...
let winner = WINNER.get_or_init(|| "Bob".to_string());
println!("{} wins!", winner);
}).unwrap();
}
Here also, a OnceLock global variable WINNER is created, but this time the WINNER is intended to hold an owned string (String). The main function creates a scoped thread using crossbeam::scope, a more advanced mechanism for spawning threads when shared state is involved. This function guarantees that all threads it spawns will finish before it returns, which can be useful when handling shared mutable state.
Within this scope, a new thread is spawned that tries to set WINNER to “Alice”. Notice here that we use let _ = WINNER.set("Alice".to_string());
to handle the Result
returned by the set
method. The underscore _
is a placeholder for a variable whose value we don’t care about. In this case, we’re ignoring the Result
value. If WINNER remains uninitialized, it gets set to “Bob” and the winner is printed out.
Conclusion
The introduction of OnceCell and OnceLock in Rust 1.70.0 is a breakthrough that promises greater efficiency and control in shared data initialization. Their uniqueness lies in their one-time initialization capability, preventing re-initialization and catering perfectly to scenarios requiring expensive operations to be performed only once. Stay tuned to Life’n’Learn for more updates from the dynamic world of programming and technology. Join us in our journey of exploring, learning, and mastering the Rust language and its powerful updates!
The addition of OnceCell and OnceLock in Rust 1.70.0 presents exciting possibilities for developers to further enhance their Rust coding prowess. At Life’n’Learn, we’re thrilled about these new features and we’re eager to see how they will be utilized by the Rust community.
Stay connected to our blog for more insights into the ever-evolving world of programming, technology updates, and of course, more Rust updates. Let’s keep coding and learning together!