1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
//!# Lazy non-const Statics
//!This crate provides a simple global allocation item to store items as statics that arent
//!necessarily constant. "Global" is a bit of a misnomer, as these are more accurately described as
//!immutable non-constant statics.
//!
//![Global] dereferences to `T`, so it can be treated as a wrapper that allows any type to be static.
//!# Usage
//![Global] stores a function pointer that produces `T`. On the first deref call, this value will be
//!produced and allocated on the heap for the lifetime of the program. Subsequent calls will return
//!this cached value.
//!```rust
//!use global::Global;
//!
//!static MY_NUM: Global<i32> = Global::new(|| 5);
//!
//!fn main() {
//! assert_eq!(*MY_NUM + 5, 10);
//!}
//!```
//!# Limitations
//!The biggest limitation is the double-pointer indirection that arises from storing a type that
//!itself allocates memory, such as [Vec] or [Box]. It also isn't possible to store DSTs, as the
//!data needs to be returned from a function on the stack. This could be fixed with a type that
//!offsets its allocation onto the producing closure, however types like [Vec] that require extra
//!information would still not work with this system.
//!
//!You may also want statics to simply be initalized on program startup,
//!which can be done with crates like ctor.
#![cfg_attr(docsrs, feature(doc_cfg))]
use std::{ops::Deref, sync::OnceLock};
#[cfg_attr(docsrs, doc(cfg(feature = "ctor")))]
#[cfg(feature = "ctor")]
pub use ctor;
#[cfg_attr(docsrs, doc(cfg(feature = "ctor")))]
#[cfg(feature = "ctor")]
#[macro_export]
/// Generate a static with a ctor procedure.
///
///```rust
///use global::ctor_static;
///
///ctor_static! {
/// static MY_NUM: i32 = { 5 };
/// static MY_OTHER_NUM: i32 = { *MY_NUM * 2 };
///};
///```
macro_rules! ctor_static {
($($name:ident: $type: ty = $init:block);*;) => {
$(static $name: Global<$type> = Global::new(|| $init););*
#[crate::ctor::ctor]
fn _global_init() {
$($name.init());*
}
};
}
///Lazily evaluated static allocation.
pub struct Global<T> {
f: fn() -> T,
data: OnceLock<SendPtr<T>>
}
struct SendPtr<T>(pub *const T);
unsafe impl<T> Send for SendPtr<T> {}
unsafe impl<T> Sync for SendPtr<T> {}
impl<T> Deref for SendPtr<T> {
type Target = *const T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> Global<T> {
///Constructs a new global.
///Rather than a value, this function takes a closure that produces a value.
///```rust
///use global::Global;
///
///static MY_TABLE: Global<Vec<&str>> = Global::new(|| vec!["a", "b", "c"]);
pub const fn new(f: fn() -> T) -> Self {
Self { f, data: OnceLock::new() }
}
///Initializes the contents of a global. Does nothing if already initialized.
pub fn init(&self) {
if let None = self.data.get() {
let _ = unsafe { self.alloc() };
}
}
///Retrieves a reference to the value inside the global without allocating.
///This function will return `None` if the global has not been allocated.
pub fn get(&self) -> Option<&T> {
self.data.get().map(|ptr| {unsafe { &***ptr }})
}
///Retrieves a reference to the value inside the global without allocating. Calling this function on
///an unallocated global is undefined behavior.
pub unsafe fn get_unchecked(&self) -> &T {
//lol
&***self.data.get().unwrap_unchecked()
}
///Caller must ensure cell has not been already allocated
unsafe fn alloc(&self) -> *const T {
//box will panic if it cannot allocate
let ptr = Box::leak(
Box::new((self.f)())
) as *const T;
self.data.set(SendPtr(ptr)).unwrap_unchecked();
**self.data.get().unwrap_unchecked()
}
}
impl<T> Deref for Global<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self.data.get() {
Some(v) => unsafe { &***v },
None => unsafe { &*self.alloc() },
}
}
}
static TEST: Global<u8> = Global::new(|| 5);
#[cfg(test)]
mod tests {
use std::ops::Add;
use super::*;
#[test]
fn it_works() {
assert_eq!(TEST.add(1), 6);
assert_eq!(*TEST, 5);
}
#[test]
#[cfg(feature = "ctor")]
fn ctor_test() {
ctor_static! {
THING: u32 = { 5 };
};
assert_eq!(THING.add(1), 6);
assert_eq!(*THING, 5);
}
}