Macro sym

Source
macro_rules! sym {
    ($sym:ident) => { ... };
    ($sym:literal) => { ... };
    (@impl $sym:expr) => { ... };
}
Expand description

Create a literal symbol from a literal identifier or string

Symbols created with the sym!(...) macro are statically allocated and deduplicated on program startup. This means that there is no discernible overhead at the point of use, making them suitable even in long chains of if statements and inner loops.

IMPORTANT: For this macro to work in a particular crate, the enable!() macro must appear exactly once in the crate’s root. This creates the global registration table at link-time.

§Safety

This macro is safe (and performant) to use everywhere, with important caveats:

  1. If you are using “static initializers” (code that runs before main(), like through the ctor crate), this macro must NOT be called in such a static initializer function. See https://github.com/mmastrac/rust-ctor/issues/159. Using Symbol::new() in such a function is fine.

  2. If you are using C-style dynamic libraries (cdylib crate type), those libraries must use the stringleton-dylib crate instead of stringleton.

  3. If you are loading dynamic libraries at runtime (i.e., outside of Cargo’s dependency graph), the host crate must also use the stringleton-dylib crate instead of stringleton.

§Low-level details

This macro creates an entry in a per-crate linkme “distributed slice”, as well as a static initializer called by the OS when the current crate is loaded at runtime (before main()), either as part of an executable or as part of a dynamic library.

On x86-64 and ARM64, this macro is guaranteed to compile into a single relaxed atomic memory load instruction from an offset in the .bss segment. On x86, relaxed atomic load instructions have no additional overhead compared to non-atomic loads.

Internally, this uses the linkme and ctor crates to register this callsite in static binary memory and initialize it on startup. However, when running under Miri (or other platforms not supported by linkme), the implementation falls back on a slower implementation that effectively calls Symbol::new() every time, which takes a global read-lock.

When the debug-assertions feature is enabled, there is an additional check that panics if the call site has not been populated by a static ctor. This assertion will only be triggered if the current platform does not support static initializers.