take_static/
lib.rs

1//! This crate provides the [`take_static`] macro to create statics that provide mutable access only once:
2//!
3//! ```
4//! use take_static::take_static;
5//!
6//! take_static! {
7//!     static NUMBER: usize = 5;
8//! }
9//!
10//! assert_eq!(NUMBER.take(), Some(&mut 5));
11//! assert_eq!(NUMBER.take(), None);
12//! ```
13//!
14//! The resulting statics are [`TakeStatic`].
15
16#![no_std]
17
18use core::cell::UnsafeCell;
19use core::fmt;
20
21use call_once::CallOnce;
22
23/// A synchronization primitive which can be accessed only once.
24///
25/// This type is a thread-safe cell that can only be used in statics.
26/// [`TakeStatic::take`] provides a mutable reference to the contents without RAII guards, but only on the first try.
27///
28/// # Similarities with `takecell::TakeCell`
29///
30/// `TakeStatic` is similar to [`takecell::TakeCell`] in that both can be used to create singletons.
31/// Additionally, `TakeCell`s can be created at runtime, making them suitable for other use-cases such as [doing work only once].
32/// `TakeStatic`, on the other hand, is specialized to creating statics through [`take_static`], allowing even `!Send` types to be used and not requiring explicitly naming the wrapper type.
33///
34/// [`takecell::TakeCell`]: https://docs.rs/takecell/0.1.1/takecell/struct.TakeCell.html
35/// [doing work only once]: https://docs.rs/takecell/0.1.1/takecell/index.html#doing-work-only-once
36///
37/// # Similarities with `cortex_m::singleton`
38///
39/// `TakeStatic` can be used similarly to [`cortex_m::singleton`] to create a mutable reference to a statically allocated value.
40/// In contrast to `cortex_m::singleton`, `TakeStatic` is thread safe.
41///
42/// [`cortex_m::singleton`]: https://docs.rs/cortex-m/0.7.7/cortex_m/macro.singleton.html
43///
44/// # Examples
45///
46/// ```
47/// use take_static::take_static;
48///
49/// take_static! {
50///     static NUMBER: usize = 5;
51/// }
52///
53/// assert_eq!(NUMBER.take(), Some(&mut 5));
54/// assert_eq!(NUMBER.take(), None);
55/// ```
56pub struct TakeStatic<T: ?Sized> {
57    taken: CallOnce,
58    data: UnsafeCell<T>,
59}
60
61impl<T: ?Sized> fmt::Debug for TakeStatic<T> {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        f.debug_struct("TakeStatic")
64            .field("taken", &self.taken)
65            .field("data", &&self.data)
66            .finish()
67    }
68}
69
70// SAFETY: This is safe even without `Send` bound,
71// as `TakeStatics` can only ever be constructed at compile-time for statics.
72// The consteval “thread” is not considered part of the `Send` contract.
73unsafe impl<T: ?Sized> Sync for TakeStatic<T> {}
74
75/// Declare a new static that provides mutable access—but only once.
76///
77/// # Syntax
78///
79/// The macro wraps any number of static declarations and makes them [`TakeStatic`].
80/// Publicity and attributes for each static are allowed. Example:
81///
82/// ```
83/// use take_static::take_static;
84///
85/// take_static! {
86///     pub static FOO: u32 = 1;
87///
88///     static BAR: f32 = 1.0;
89/// }
90///
91/// assert_eq!(FOO.take(), Some(&mut 1));
92/// assert_eq!(BAR.take(), Some(&mut 1.0));
93/// ```
94///
95/// See [`TakeStatic`] documentation for more information.
96// Modeled after `thread_local` and `lazy_static`.
97#[macro_export]
98macro_rules! take_static {
99    // empty (base case for the recursion)
100    () => {};
101
102    // process multiple declarations
103    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => (
104        $crate::take_static!($(#[$attr])* $vis static $name: $t = $init);
105        $crate::take_static!($($rest)*);
106    );
107
108    // handle a single declaration
109    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => (
110        $(#[$attr])* $vis static $name: $crate::TakeStatic<$t> = {
111            const INIT_EXPR: $t = $init;
112            // SAFETY: this initializes a static.
113            unsafe { $crate::TakeStatic::new(INIT_EXPR) }
114        };
115    );
116}
117
118impl<T> TakeStatic<T> {
119    /// Creates a new `TakeStatic` containing the given value.
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// use take_static::TakeStatic;
125    ///
126    /// static TAKE_STATIC: TakeStatic<usize> = unsafe { TakeStatic::new(5) };
127    /// ```
128    ///
129    /// # Safety
130    ///
131    /// This function may only be used to initialize a static item.
132    /// This is due to the unconditional `Sync` impl of `TakeStatic`.
133    #[doc(hidden)]
134    #[inline]
135    pub const unsafe fn new(val: T) -> Self {
136        Self {
137            taken: CallOnce::new(),
138            data: UnsafeCell::new(val),
139        }
140    }
141}
142
143impl<T: ?Sized> TakeStatic<T> {
144    /// Takes the mutable reference to the wrapped value.
145    ///
146    /// Only the first call returns `Some`.
147    /// All subsequent calls return `None`.
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// use take_static::take_static;
153    ///
154    /// take_static! {
155    ///     static TAKE_STATIC: usize = 5;
156    /// }
157    ///
158    /// let number = TAKE_STATIC.take().unwrap();
159    /// assert_eq!(number, &mut 5);
160    /// assert!(TAKE_STATIC.take().is_none());
161    /// ```
162    #[inline]
163    #[must_use]
164    pub fn take(&self) -> Option<&mut T> {
165        self.taken
166            .call_once()
167            .ok()
168            .map(|_| unsafe { &mut *self.data.get() })
169    }
170
171    /// Returns `true` if the mutable reference has been taken.
172    ///
173    /// # Examples
174    ///
175    /// ```
176    /// use take_static::take_static;
177    ///
178    /// take_static! {
179    ///     static TAKE_STATIC: usize = 5;
180    /// }
181    ///
182    /// assert!(!TAKE_STATIC.is_taken());
183    /// let number = TAKE_STATIC.take().unwrap();
184    /// assert!(TAKE_STATIC.is_taken());
185    /// ```
186    #[inline]
187    pub fn is_taken(&self) -> bool {
188        self.taken.was_called()
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195
196    #[test]
197    fn visibility() {
198        take_static! {
199            static SELF: u8 = 3;
200        }
201
202        mod module {
203            use super::*;
204
205            take_static! {
206                static _SELF: u8 = 3;
207                pub(super) static SUPER: u8 = 3;
208                pub(crate) static CRATE: u8 = 3;
209                pub static PUB: u8 = 3;
210            }
211        }
212
213        assert_eq!(SELF.take(), Some(&mut 3));
214        assert_eq!(module::SUPER.take(), Some(&mut 3));
215        assert_eq!(module::CRATE.take(), Some(&mut 3));
216        assert_eq!(module::PUB.take(), Some(&mut 3));
217    }
218
219    #[test]
220    fn always_sync() {
221        use core::ptr;
222
223        take_static! {
224            static FOO: *mut u8 = ptr::null_mut();
225        }
226        assert_eq!(FOO.take(), Some(&mut ptr::null_mut()));
227    }
228
229    #[test]
230    fn init_block() {
231        take_static! {
232            static FOO: u8 = {
233                let value = 1 + 2;
234                value
235            };
236        }
237        assert_eq!(FOO.take(), Some(&mut 3));
238    }
239
240    #[test]
241    fn single_declaration() {
242        take_static!(static FOO: u8 = 3);
243        assert_eq!(FOO.take(), Some(&mut 3));
244    }
245}