# static-generics
[](https://docs.rs/static-generics)
[](LICENSE-MIT)
Zero-cost generic statics for Rust.
Based on the ideas of [cynecx/generic-statics](https://github.com/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.
```rust
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.