I'm writing a chess engine in Rust and want to start using Zobrist Hashing, which requires me to pre-initialize an array of pseudo-random numbers from a constant seed.
Since this array won't change, I tried to initialize it in a static global array generated at compile time like this:
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
use rand::RngCore;
pub static ZOBRIST_SEEDS: [u64; 781] = generate_zobrist_seeds();
const fn generate_zobrist_seeds() -> [u64; 781] {
let mut result = [0; 781];
let mut rng = ChaCha8Rng::seed_from_u64(4174800045971885562);
let mut i = 0;
while i < 781 {
result[i] = rng.next_u64();
i+= 1;
}
result
}
This doesn't work since ChaCha8Rng::seed_from_u64
and ChaCha8Rng::next_u64
are non-constants. Is there any way I could generate this array at compile time, or should I just do it at the start of my runtime, I know the const_random!
macro exists, but this doesn't meet my needs since I need the values to stay the same every time the program is built.
First, I implore you consider very carefully: do you actually want a random seed generated at compile time? I would advise against it in most circumstances because of a number of problems, primary of which is it will make your build non-reproducible (there are other blockers in Rust to a reproducible build at the moment, but let's not "send the helve after the hatchet", so to speak).
Given the particular context you have provided, I would generate the seed at runtime were it to be my decision. To that effect, this is what I would write:
pub static ZOBRIST_SEEDS: LazyLock<[u64; 781]> = LazyLock::new(|| generate_zobrist_seeds());
// generate_zobrist_seeds needs not be const
This gives you an immutable, lazily initialised runtime value which you can use as if it's a const
(technically there is a semantic difference: static
is a memory location while const
is a value, but in practise using them at the call-site feels very similar).
On the other hand, if you insist on generating this seed at compile time, you have 3 options. From the least effort to most:
comptime
crateThis crate allows you to run arbitrary code at compile time. In effect it relaxes Rust's restriction of "only const
functions is const
context".
The downside is you have to introduce a new direct dependency.
Basically, you generate the string pub const ZOBRIST_SEEDS: [u64; 781] = [ /* ... */ ];
in your build script, write it to a file in OUT_DIR
, then include!()
it. Here is something I wrote in a public repo that does this: example.
It's basically #1 but you are hand-rolling everything. Definitely overkill for what you are trying to accomplish. I wouldn't recommend it.