use once_cell; // 1.17.0
use rand; // 0.8.5
use once_cell::sync::{OnceCell, Lazy};
use rand::rngs::StdRng;
use rand::{SeedableRng, Rng};
static A: OnceCell<i32> = OnceCell::new();
static B: OnceCell<i32> = OnceCell::new();
fn init() {
let mut r = StdRng::seed_from_u64(12345);
A.set(r.gen()).unwrap();
B.set(r.gen()).unwrap();
}
fn main() {
init();
dbg!((A.get().unwrap(), B.get().unwrap()));
}
By trying to be more idiomatic (and remove a lot of .get().unwrap()) , I wanted to switch from OnceCell to Lazy since after their initialization neither A or B will change. The problem is that their initialization is order dependent, and main has to remain the same (so I cannot make a Lazy<(i32, i32)>) so the only solution I see would be to have something like static destructuring:
use once_cell; // 1.17.0
use rand; // 0.8.5
use once_cell::sync::{OnceCell, Lazy};
use rand::rngs::StdRng;
use rand::{SeedableRng, Rng};
static (A, B): Lazy<(i32, i32)> = Lazy::new(|| init());
fn init() {
let mut r = StdRng::seed_from_u64(12345);
A.set(r.gen()).unwrap();
B.set(r.gen()).unwrap();
(A, B)
}
fn main() {
init();
dbg!((A.get().unwrap(), B.get().unwrap()));
}
How would you achieve this ? Or maybe it's unnecessary and OnceCell are already good enough ?
What do you mean by this? If you mean that A and B must be initialized in that order, then that's exactly what you achieve by a Lazy<(i32, i32)>. Accordingly, this works predictably.
If you mean that for some weird reason, you need to syntactically preserve the variable names A and B as separate items, then just move the side-effect-less field getter logic into them, and let the initializer logic remain atomic.
Why couldn't you? The point of a Lazy is that it allows you to execute arbitrary code upon first access. Surely that includes accessing other globals.
The definition of Lazy<T> says that the initializer must satisfy FnOnce() -> T, i.e., a function returning a T, and in particular the default initializer type is fn() -> T, i.e., a pointer to a function that returns a value of the given type. This doesn't forbid accessing other statics in the body of the initializer. It's just a regular, plain old function.