static-generics 0.1.3

Zero-cost generic statics for Rust.
Documentation
#![no_std]
#![doc = include_str!("../README.md")]
#![cfg_attr(
    all(feature = "nightly", target_family = "wasm", target_arch = "wasm32"),
    feature(asm_experimental_arch)
)]

#[cfg(all(feature = "std"))]
#[allow(dead_code)]
pub(crate) mod fallback;
pub mod namespace;
pub mod once;

pub use bytemuck::{Pod, Zeroable};
pub use namespace::{Namespace, NamespaceExt};
pub use once::OnceSlot;

define_namespace!(pub DefaultNamespace);

/// Generic static in the [`DefaultNamespace`].
///
/// ```rust
/// use core::sync::atomic::{AtomicU32, Ordering};
///
/// let a = static_generics::get::<AtomicU32>();
/// a.store(7, Ordering::Relaxed);
/// assert_eq!(static_generics::get::<AtomicU32>().load(Ordering::Relaxed), 7);
/// ```
#[inline(always)]
#[must_use]
pub fn get<T>() -> &'static T
where
    T: 'static + Zeroable,
{
    <DefaultNamespace as Namespace>::generic_static::<T>()
}

/// Generic static in an explicit namespace.
#[inline(always)]
#[must_use]
pub fn get_in<NS, T>() -> &'static T
where
    NS: Namespace,
    T: 'static + Zeroable,
{
    <NS as Namespace>::generic_static::<T>()
}

/// Raw (`*mut T`) view of the generic static in the [`DefaultNamespace`].
///
/// Same slot as [`get`]; the `static mut` equivalent.
///
/// # Safety
///
/// See [`Namespace::generic_static_mut`].
#[inline(always)]
pub unsafe fn get_mut<T>() -> *mut T
where
    T: 'static + Zeroable,
{
    // SAFETY: forwarded from the caller upholding the `static mut` contract.
    unsafe { <DefaultNamespace as Namespace>::generic_static_mut::<T>() }
}

/// Raw (`*mut T`) view of the generic static in an explicit namespace.
///
/// # Safety
///
/// See [`Namespace::generic_static_mut`].
#[inline(always)]
pub unsafe fn get_in_mut<NS, T>() -> *mut T
where
    NS: Namespace,
    T: 'static + Zeroable,
{
    // SAFETY: forwarded from the caller upholding the `static mut` contract.
    unsafe { <NS as Namespace>::generic_static_mut::<T>() }
}

/// Generic const-keyed static in the [`DefaultNamespace`].
#[inline(always)]
#[must_use]
pub fn get_const<T, const N: usize>() -> &'static T
where
    T: 'static + Zeroable,
{
    <DefaultNamespace as Namespace>::generic_static_const::<T, N>()
}

/// Generic const-keyed static in an explicit namespace.
#[inline(always)]
#[must_use]
pub fn get_in_const<NS, T, const N: usize>() -> &'static T
where
    NS: Namespace,
    T: 'static + Zeroable,
{
    <NS as Namespace>::generic_static_const::<T, N>()
}

/// Raw (`*mut T`) const-keyed view in the [`DefaultNamespace`].
///
/// # Safety
///
/// See [`Namespace::generic_static_mut`].
#[inline(always)]
pub unsafe fn get_const_mut<T, const N: usize>() -> *mut T
where
    T: 'static + Zeroable,
{
    // SAFETY: forwarded from the caller upholding the `static mut` safety.
    unsafe { <DefaultNamespace as Namespace>::generic_static_const_mut::<T, N>() }
}

/// Raw (`*mut T`) const-keyed view in an explicit namespace.
///
/// # Safety
///
/// See [`Namespace::generic_static_mut`].
#[inline(always)]
pub unsafe fn get_in_const_mut<NS, T, const N: usize>() -> *mut T
where
    NS: Namespace,
    T: 'static + Zeroable,
{
    // SAFETY: forwarded from the caller upholding the `static mut` contract.
    unsafe { <NS as Namespace>::generic_static_const_mut::<T, N>() }
}

/// Lazily-initialized generic static in the [`DefaultNamespace`].
///
/// Backed by [`OnceSlot`]; the first call with a given `T` runs `init`.
/// See [`NamespaceExt::once`].
///
/// ## Precautions
///
/// This function is not marked unsafe, but it can cause some unforeseen effects:
///
/// If you fetch the `once::<T>(A)` with `A` being initializer in one place and then fetch `once::<T>(B)` with B being different initializer,
/// it will initialize with whatever invocation was first.
#[must_use]
pub fn once<T>(init: impl FnOnce() -> T) -> &'static T
where
    T: 'static + Send + Sync,
{
    <DefaultNamespace as NamespaceExt>::once(init)
}

/// Lazily-initialized const-keyed generic static in the [`DefaultNamespace`].
#[must_use]
pub fn once_const<T, const N: usize>(init: impl FnOnce() -> T) -> &'static T
where
    T: 'static + Send + Sync,
{
    <DefaultNamespace as NamespaceExt>::once_const::<T, N>(init)
}