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}