Skip to main content

Crate static_generics

Crate static_generics 

Source
Expand description

§static-generics

docs.rs License: MIT OR Apache-2.0

Zero-cost generic statics for Rust.

Based on the ideas of cynecx/generic-statics, and implemented to be zero-cost*: one–two instructions to access a generic (lea, adrp+add, auipc+addi, larl, …) instead of a call to the accessor function.

*Zero cost applies only to supported platforms, unsupported platforms go through fallback of HashMap + Mutex and require std feature to be enabled.

use static_generics::{define_namespace, namespace::NamespaceExt};
use core::sync::atomic::{AtomicU64, Ordering};

define_namespace!(Counters);

let hits = Counters::get::<AtomicU64>();
hits.fetch_add(1, Ordering::Relaxed);

§Why

Rust has no generic statics unlike C++, and default solution to that is using typemap/HashMap<TypeId, ...> which require locking, allocation, hashing on each access which is expensive and slow. static-generics implements zero-cost solution which allows to implement generic statics in ~1-2 instructions on supported platforms (and falls back to hashmap on unsupported).

§Requirements and limits

  • Only bytemuck::Zeroable types can be stored in the static generic (use OnceSlot to lazily initialize any other types in static generic).
  • No Drop support, all static generics are essentially leaked. If you need to clean-up memory after them, it has to be done manually.
  • Within a single crate the same (Namespace, T) is always the same address. Across crates or dynamic libraries sharing is not guaranteed: depending on optimization and linking, another crate may alias your slot or keep its own copy. Dynamic library loading in particular is not guaranteed to return the same address for generic_static::<i32>() done in libA.so and libB.so.

§Implementation

We use inline assembly + monomorphization of rustc to emit “unique” function body per each T. In the assembly code block we define storage with weak + COMDAT linkage in a per-symbol .bss section with the name of unique function

  • gen_static_ prefix, and after that we compute the PC-relative address using lea on x86, adrp on arm, and so on.

Since the storage symbol is linkonce, the linker keeps a single copy per binary, so inlined generic_static calls stay at 1 or 2 instructions with no runtime overhead.

§Platform support

  • x86_64
  • aarch64
  • x86_32
  • arm (Thumb as well)
  • riscv32/riscv64
  • loongarc32/loongarch64
  • powerpc/powerpc64
  • s390x
  • wasm32: ONLY with nightly feature enabled, stable Rust does not support asm!() on WASM.

On other platforms (or with Miri/Cranelift detected) the crate will fallback to slow implementation via HashMap and Mutex.

Re-exports§

pub use namespace::Namespace;
pub use namespace::NamespaceExt;
pub use once::OnceSlot;

Modules§

namespace
Namespaced generic statics with zero-cost access.
once
Lazy, one-time initialization on top of generic_static.

Macros§

define_namespace
Define a namespace for static generics. Namespace is like “scope” for generics. Were you to always use DefaultNamespace you would end up with eventually running out of static slots. Defining multiple namespaces allows you to have virtually unlimited set of static generics per each usage.
define_static
Declare a typed accessor function for one generic static.

Structs§

DefaultNamespace

Traits§

Pod
Marker trait for “plain old data”.
Zeroable
Trait for types that can be safely created with zeroed.

Functions§

get
Generic static in the DefaultNamespace.
get_const
Generic const-keyed static in the DefaultNamespace.
get_const_mut
Raw (*mut T) const-keyed view in the DefaultNamespace.
get_in
Generic static in an explicit namespace.
get_in_const
Generic const-keyed static in an explicit namespace.
get_in_const_mut
Raw (*mut T) const-keyed view in an explicit namespace.
get_in_mut
Raw (*mut T) view of the generic static in an explicit namespace.
get_mut
Raw (*mut T) view of the generic static in the DefaultNamespace.
once
Lazily-initialized generic static in the DefaultNamespace.
once_const
Lazily-initialized const-keyed generic static in the DefaultNamespace.