Note: I know about this FAQ entry in the old docs, but I think this problem still may be worth revisiting.
The most common advice when runstime initialization of static
s is needed is to use crates like lazy_static
. Such solutions work well enough in most of the cases, but in some rare cases they are quite sub-optimal. Under the hood they use synchronization primitives (usually atomics) to track if initialization has been performed. This means, that each use of such static has to include an initialization check branch. Not only it can be relatively expensive in hot loops, but more importantly it prevents compiler from applying some important optimizations (e.g. see this post, it's about atomics, but the same applies to lazy statics as well). Rust std
itself suffers from the same issue in its is_x86_feature_detected
macro.
Runtime static initialization may look roughly like this:
// the memory behind this static will be initially filled with zeros
#[init]
static FOO: u32 = || { .. };
// in the case of error main will return an error code by
// using the `Termination` trait
#[try_init]
pub static Bar: Vec<u32> = || {
if flag {
Ok(val)
} else {
Err(Error)
}
};
IIUC the "static initialization order fiasco" can be mostly solved by forbidding initialization functions. In theory we may extended this feature to allow explicit specification of dependencies between statics initialized at runtime.
Instead of relying on linker, it may be worth for Rust to handle this initialization by itself, i.e. it could collect all such statics from dependencies and implicitly insert calls to initialization functions into beginning of the project's main
.
Disadvantages of adding such feature are clear: additional compiler complexity and implicitly running code, which may cause unpleasant surprises (e.g. if abused, it may significantly increase program starting up time). Also in contrast with the "lazy" statics approach, static initialization code will be executed even if its value is not used anywhere.
But in some performance sensitive cases we need to know that a static
was properly initialized without checking it by ourselves, which I think warrants addition of such feature. In some cases it may be worked around by caching its value on stack or by using initialization tokens, but it does not always work good enough (e.g. if static
is too big or if we pass crate boundaries, which prevents API changes).
While my main concern is performance sensitive code, such statics may be useful for other cases as well.
What do you think about possibility of introducing life-before-main in the proposed form?