# \[Discussion\] Adding an \`init\` function that could modify static variables directly

**URL:** <https://internals.rust-lang.org/t/discussion-adding-an-init-function-that-could-modify-static-variables-directly/19000>\
**Category:** language design\
**Created:** [June 16, 2023, 2:18am UTC](https://internals.rust-lang.org/t/discussion-adding-an-init-function-that-could-modify-static-variables-directly/19000 "2023-06-16T02:18:41Z")\
**Posts on this page:** 1\
**Showing post:** 52

<div class="post-metadata">

**Author:** ![farnz](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/farnz/32/6587_2.png) [@farnz](https://internals.rust-lang.org/u/farnz)\
**Post date:** [June 21, 2023, 7:59pm UTC](https://internals.rust-lang.org/t/discussion-adding-an-init-function-that-could-modify-static-variables-directly/19000/52 "2023-06-21T19:59:14Z")

</div>

> [@NoamB](#):
>
> I think that restricting the importing crate is intractable - that's why I suggested restricting the exporting crate. This would allow low level crates which know about each other to do the cyclic dependency trickery.

That's the underlying challenge, though - it's the importing crates that have all the control and power, right up until they call code in the exporting crate. Nothing stops C from calling `A::needs_init` or `B::calls_dependent` before A's static initialization code runs, especially once you consider environments like WebAssembly.

This is what lazy initialization works around, at the expense of 3 instructions per global access - it ensures that when C calls `A::needs_init` or `B::calls_dependent`, A's initializer runs, because it's run lazily on demand. And if having the first call to `A::needs_init` or `B::calls_dependent` run slow is not always acceptable, A can export a `eager_init` function that does all the lazy init work up-front.

Part of the question, though, is how much work we actually ought to do for this case - if you _can_ pass a global context by reference, instead of having it found lurking in the environment, you get performance benefits where it enables extra optimizations, and you get to have multiple global contexts in one program. If you can't pass a reference to the context, but can add a parameter, you get the ZST init token trick. If you can afford 3 instructions when looking up your context, you can use `OnceLock` or `LazyLock`.

And there are at least two directions that I'm aware of that people have thought seriously about which would be more general. One is just doing a better job of VRA, so that the 3 instruction penalty for `OnceLock` and friends goes away if it's possible for the compiler to see that it's been initialized already, just as the 3 instruction penalty for slice indexing goes away if the compiler can see that the slice is clearly large enough; the other is [capabilities/contexts](https://internals.rust-lang.org/t/blog-post-contexts-and-capabilities-in-rust/15833) which would provide a way to pass the global context around without requiring it to be a parameter.

---

_[View the full topic](https://internals.rust-lang.org/t/discussion-adding-an-init-function-that-could-modify-static-variables-directly/19000)._
