interrupt_ref_cell/
local_key.rs

1use std::thread::LocalKey;
2
3use crate::InterruptRefCell;
4
5pub trait LocalKeyExt<T> {
6    /// Acquires a reference to the contained value.
7    ///
8    /// This will lazily initialize the value if this thread has not referenced
9    /// this key yet.
10    ///
11    /// # Panics
12    ///
13    /// Panics if the value is currently mutably borrowed.
14    ///
15    /// Panics if the key currently has its destructor running,
16    /// and it **may** panic if the destructor has previously been run for this thread.
17    ///
18    /// # Example
19    ///
20    /// ```
21    /// use interrupt_ref_cell::{InterruptRefCell, LocalKeyExt};
22    ///
23    /// thread_local! {
24    ///     static X: InterruptRefCell<Vec<i32>> = InterruptRefCell::new(Vec::new());
25    /// }
26    ///
27    /// X.with_borrow(|v| assert!(v.is_empty()));
28    /// ```
29    fn with_borrow<F, R>(&'static self, f: F) -> R
30    where
31        F: FnOnce(&T) -> R;
32
33    /// Acquires a mutable reference to the contained value.
34    ///
35    /// This will lazily initialize the value if this thread has not referenced
36    /// this key yet.
37    ///
38    /// # Panics
39    ///
40    /// Panics if the value is currently borrowed.
41    ///
42    /// Panics if the key currently has its destructor running,
43    /// and it **may** panic if the destructor has previously been run for this thread.
44    ///
45    /// # Example
46    ///
47    /// ```
48    /// use interrupt_ref_cell::{InterruptRefCell, LocalKeyExt};
49    ///
50    /// thread_local! {
51    ///     static X: InterruptRefCell<Vec<i32>> = InterruptRefCell::new(Vec::new());
52    /// }
53    ///
54    /// X.with_borrow_mut(|v| v.push(1));
55    ///
56    /// X.with_borrow(|v| assert_eq!(*v, vec![1]));
57    /// ```
58    fn with_borrow_mut<F, R>(&'static self, f: F) -> R
59    where
60        F: FnOnce(&mut T) -> R;
61
62    /// Sets the contained value.
63    ///
64    /// <div class="warning">This will run the lazy initializer.</div>
65    ///
66    /// Unlike [`LocalKey<RefCell<T>>::set`], this method *does* run the lazy
67    /// initializer of the thread local. The required API to avoid that is not
68    /// not public.
69    ///
70    /// # Panics
71    ///
72    /// Panics if the value is currently borrowed.
73    ///
74    /// Panics if the key currently has its destructor running,
75    /// and it **may** panic if the destructor has previously been run for this thread.
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// use interrupt_ref_cell::{InterruptRefCell, LocalKeyExt};
81    ///
82    /// thread_local! {
83    ///     static X: InterruptRefCell<Vec<i32>> = InterruptRefCell::new(Vec::new());
84    /// }
85    ///
86    /// // Calling X.with() here would result in a panic.
87    ///
88    /// X.set(vec![1, 2, 3]); // But X.set() is fine, as it skips the initializer above.
89    ///
90    /// X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3]));
91    /// ```
92    fn set(&'static self, value: T);
93
94    /// Takes the contained value, leaving `Default::default()` in its place.
95    ///
96    /// This will lazily initialize the value if this thread has not referenced
97    /// this key yet.
98    ///
99    /// # Panics
100    ///
101    /// Panics if the value is currently borrowed.
102    ///
103    /// Panics if the key currently has its destructor running,
104    /// and it **may** panic if the destructor has previously been run for this thread.
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// use interrupt_ref_cell::{InterruptRefCell, LocalKeyExt};
110    ///
111    /// thread_local! {
112    ///     static X: InterruptRefCell<Vec<i32>> = InterruptRefCell::new(Vec::new());
113    /// }
114    ///
115    /// X.with_borrow_mut(|v| v.push(1));
116    ///
117    /// let a = X.take();
118    ///
119    /// assert_eq!(a, vec![1]);
120    ///
121    /// X.with_borrow(|v| assert!(v.is_empty()));
122    /// ```
123    fn take(&'static self) -> T
124    where
125        T: Default;
126
127    /// Replaces the contained value, returning the old value.
128    ///
129    /// # Panics
130    ///
131    /// Panics if the value is currently borrowed.
132    ///
133    /// Panics if the key currently has its destructor running,
134    /// and it **may** panic if the destructor has previously been run for this thread.
135    ///
136    /// # Examples
137    ///
138    /// ```
139    /// use interrupt_ref_cell::{InterruptRefCell, LocalKeyExt};
140    ///
141    /// thread_local! {
142    ///     static X: InterruptRefCell<Vec<i32>> = InterruptRefCell::new(Vec::new());
143    /// }
144    ///
145    /// let prev = X.replace(vec![1, 2, 3]);
146    /// assert!(prev.is_empty());
147    ///
148    /// X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3]));
149    /// ```
150    fn replace(&'static self, value: T) -> T;
151}
152
153impl<T: 'static> LocalKeyExt<T> for LocalKey<InterruptRefCell<T>> {
154    fn with_borrow<F, R>(&'static self, f: F) -> R
155    where
156        F: FnOnce(&T) -> R,
157    {
158        self.with(|cell| f(&cell.borrow()))
159    }
160
161    fn with_borrow_mut<F, R>(&'static self, f: F) -> R
162    where
163        F: FnOnce(&mut T) -> R,
164    {
165        self.with(|cell| f(&mut cell.borrow_mut()))
166    }
167
168    fn set(&'static self, value: T) {
169        // We'd rather use `RefCell::initialize_with`, which is private.
170        self.with(|cell| {
171            *cell.borrow_mut() = value;
172        });
173    }
174
175    fn take(&'static self) -> T
176    where
177        T: Default,
178    {
179        self.with(|cell| cell.take())
180    }
181
182    fn replace(&'static self, value: T) -> T {
183        self.with(|cell| cell.replace(value))
184    }
185}