I'd use a global atomic integer and not use lazy_static. You don't need the atomicity in wasm, but it doesn't hurt you either, and avoiding lazy_static gives you some (insignificant) performance back. Plus for free your code works with threads.
Future tip: Rust is a very uniform language. Wherever you can put an item, there's a good chance you can put any other item there, too. You can put functions inside functions, modules inside functions, heck, even modules inside a block that is the initializer expression of a static – playground.
This is not a coincidence: it makes writing code much easier, implementation details more elegant to hide, and a lot of useful macro-based tricks possible at all.
It requires checking on every dereference whether it had been initialized. It's essentially like the C pattern of setting a pointer to null and then on every use checking if it's null and initialized it if that is the case.
does this move the initialization to first-call time, as this would do in C++? (i'd venture guess not, because if so it's essentially the same as lazy_static)
A static has no initialization code to run. This is why you can only assign it constants or outputs from a const fn, because otherwise it is not able to hard code the initial value in the binary.
Rust does typically not have code that runs before main.