#![no_std]
use core::cell::UnsafeCell;
use core::fmt;
use call_once::CallOnce;
pub struct TakeStatic<T: ?Sized> {
taken: CallOnce,
data: UnsafeCell<T>,
}
impl<T: ?Sized> fmt::Debug for TakeStatic<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TakeStatic")
.field("taken", &self.taken)
.field("data", &&self.data)
.finish()
}
}
unsafe impl<T: ?Sized> Sync for TakeStatic<T> {}
#[macro_export]
macro_rules! take_static {
() => {};
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => (
$crate::take_static!($(#[$attr])* $vis static $name: $t = $init);
$crate::take_static!($($rest)*);
);
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => (
$(#[$attr])* $vis static $name: $crate::TakeStatic<$t> = {
const INIT_EXPR: $t = $init;
unsafe { $crate::TakeStatic::new(INIT_EXPR) }
};
);
}
impl<T> TakeStatic<T> {
#[doc(hidden)]
#[inline]
pub const unsafe fn new(val: T) -> Self {
Self {
taken: CallOnce::new(),
data: UnsafeCell::new(val),
}
}
}
impl<T: ?Sized> TakeStatic<T> {
#[inline]
#[must_use]
pub fn take(&self) -> Option<&mut T> {
self.taken
.call_once()
.ok()
.map(|_| unsafe { &mut *self.data.get() })
}
#[inline]
pub fn is_taken(&self) -> bool {
self.taken.was_called()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn visibility() {
take_static! {
static SELF: u8 = 3;
}
mod module {
use super::*;
take_static! {
static _SELF: u8 = 3;
pub(super) static SUPER: u8 = 3;
pub(crate) static CRATE: u8 = 3;
pub static PUB: u8 = 3;
}
}
assert_eq!(SELF.take(), Some(&mut 3));
assert_eq!(module::SUPER.take(), Some(&mut 3));
assert_eq!(module::CRATE.take(), Some(&mut 3));
assert_eq!(module::PUB.take(), Some(&mut 3));
}
#[test]
fn always_sync() {
use core::ptr;
take_static! {
static FOO: *mut u8 = ptr::null_mut();
}
assert_eq!(FOO.take(), Some(&mut ptr::null_mut()));
}
#[test]
fn init_block() {
take_static! {
static FOO: u8 = {
let value = 1 + 2;
value
};
}
assert_eq!(FOO.take(), Some(&mut 3));
}
#[test]
fn single_declaration() {
take_static!(static FOO: u8 = 3);
assert_eq!(FOO.take(), Some(&mut 3));
}
}