Hidden inside std::collections::hash_map
is a struct called RandomState
. It's job is to initialize a DefaultHasher
with a random seed for use in a HashMap
. It turns out that if you create a random DefaultHasher
and extract the final hash without actually hashing anything, you get a random number.
fn rand() -> u64 {
RandomState::new().build_hasher().finish()
}
fn main() {
for _ in 0..5 {
println!("{}", rand());
}
}
13850751674286760248
45510352149189357
6403375774259711286
1678796842412519869
12232603414372688969
Under the hood RandomState::new
doesn't actually fetch entropy every time it's called. Instead, it caches a key per thread and increments it.
impl RandomState {
/// Constructs a new `RandomState` that is initialized with random keys.
pub fn new() -> RandomState {
thread_local!(static KEYS: Cell<(u64, u64)> = {
Cell::new(sys::hashmap_random_keys()) // e.g. getrandom()
});
KEYS.with(|keys| {
let (k0, k1) = keys.get();
keys.set((k0.wrapping_add(1), k1));
RandomState { k0, k1 }
})
}
}
This seed is then hashed with SipHash13, Rust's default hasher, and as it turns out, a pretty good pseudo random number generator.
impl hash::Hasher for SipHasher {
// ...
fn finish(&self) -> u64 {
let mut state = self.state;
let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail;
state.v3 ^= b;
Sip13Rounds::c_rounds(&mut state);
state.v0 ^= b;
state.v2 ^= 0xff;
Sip13Rounds::d_rounds(&mut state);
state.v0 ^ state.v1 ^ state.v2 ^ state.v3
}
}
Of course everything above is an implementation detail that you can't rely on... so use it at your own risk.