manually_static/
stack_and_ref.rs

1use std::ops::Deref;
2#[cfg(debug_assertions)]
3use std::sync::atomic::{AtomicBool, Ordering};
4#[cfg(debug_assertions)]
5use std::sync::Arc;
6
7/// `ManuallyStatic<T>` holds a value `T` and provides references to it
8/// with checking with `debug_assertions`.
9///
10/// In debug builds, it tracks if the original `ManuallyStatic` instance
11/// has been dropped, causing any derived `ManuallyStaticRef`
12/// to panic upon dereference if the original
13/// owner is no longer alive.
14///
15/// This is useful for simulating a 'static lifetime where you want
16/// runtime checks for use-after-free in debug environments.
17///
18/// # Example
19///
20/// ```rust
21/// use manually_static::ManuallyStatic;
22/// use std::thread;
23/// use std::time::Duration;
24///
25/// struct AppConfig {
26///     version: String,
27/// }
28///
29/// let config = ManuallyStatic::new(AppConfig {
30///     version: String::from("1.0.0"),
31/// });
32///
33/// // Get a 'static reference to the config.
34/// // This is where ManuallyStatic shines, allowing us to pass
35/// // a reference that the compiler would normally complain about
36/// // without complex ownership transfers or Arc for simple reads.
37/// let config_ref = config.get_ref();
38///
39/// let handle = thread::spawn(move || {
40///     // In this thread, we can safely access the config via the 'static reference.
41///     // In debug builds, if `config` (the original ManuallyStatic) was dropped
42///     // before this thread accessed it, it would panic.
43///
44///     thread::sleep(Duration::from_millis(100)); // Simulate some work
45///
46///     println!("Thread: App Version: {}", config_ref.version);
47/// });
48///
49/// handle.join().unwrap();
50///
51/// // config is dropped here after the thread has finished
52/// ```
53pub struct ManuallyStatic<T> {
54    value: T,
55    /// This flag is only present in debug builds (`cfg(debug_assertions)`).
56    /// It is set to `true` when the `ManuallyStatic` instance is dropped.
57    #[cfg(debug_assertions)]
58    was_dropped: Arc<AtomicBool>,
59}
60
61impl<T> ManuallyStatic<T> {
62    /// Creates a new `ManuallyStatic` instance holding the given value.
63    pub fn new(value: T) -> Self {
64        Self {
65            value,
66            #[cfg(debug_assertions)]
67            was_dropped: Arc::new(AtomicBool::new(false)),
68        }
69    }
70
71    /// Returns a [`ManuallyStaticRef`] that provides immutable access to the
72    /// contained value. In debug builds, dereferencing this will panic
73    /// if the original `ManuallyStatic` instance has been dropped.
74    pub fn get_ref(&self) -> ManuallyStaticRef<T> {
75        ManuallyStaticRef {
76            value_ref: &self.value,
77            #[cfg(debug_assertions)]
78            was_dropped: self.was_dropped.clone(),
79        }
80    }
81}
82
83/// Implements the `Drop` trait for [`ManuallyStatic<T>`] only in debug builds.
84/// When [`ManuallyStatic`] is dropped, it sets the `was_dropped` flag to `true`.
85#[cfg(debug_assertions)]
86impl<T> Drop for ManuallyStatic<T> {
87    fn drop(&mut self) {
88        self.was_dropped.store(true, Ordering::Release);
89    }
90}
91
92/// A reference to the value held by [`ManuallyStatic<T>`].
93/// In debug builds, it will panic if dereferenced after the
94/// original [`ManuallyStatic`] has been dropped.
95pub struct ManuallyStaticRef<T> {
96    value_ref: *const T,
97    #[cfg(debug_assertions)]
98    was_dropped: Arc<AtomicBool>,
99}
100
101impl<T> Deref for ManuallyStaticRef<T> {
102    type Target = T;
103
104    fn deref(&self) -> &T {
105        #[cfg(debug_assertions)]
106        {
107            assert!(
108                !self.was_dropped.load(Ordering::Acquire),
109                "Attempted to dereference ManuallyStaticRef after ManuallyStatic was dropped!"
110            );
111        }
112
113        unsafe { &*self.value_ref }
114    }
115}
116
117unsafe impl<T: Send> Send for ManuallyStaticRef<T> {}
118unsafe impl<T: Sync> Sync for ManuallyStaticRef<T> {}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn test_manually_static_ref_access() {
126        let ms = ManuallyStatic::new(42);
127        let ms_ref = ms.get_ref();
128
129        assert_eq!(*ms_ref, 42);
130    }
131
132    #[test]
133    #[cfg(debug_assertions)] // This test only runs in debug mode
134    #[should_panic(
135        expected = "Attempted to dereference ManuallyStaticRef after ManuallyStatic was dropped!"
136    )]
137    fn test_manually_static_ref_panics_on_drop() {
138        let ms_ref;
139
140        {
141            let ms = ManuallyStatic::new(42);
142
143            ms_ref = ms.get_ref();
144        }
145
146        let _ = *ms_ref;
147    }
148
149    #[test]
150    #[cfg(not(debug_assertions))] // This test only runs in release mode
151    fn test_manually_static_ref_no_panic_on_drop_release_mode() {
152        let ms_ptr;
153
154        {
155            let ms = ManuallyStatic::new(42);
156
157            ms_ptr = ms.get_ref();
158        }
159
160        let _ = *ms_ptr;
161    }
162}