orx_concurrent_option/exclusive.rs
1use crate::{ConcurrentOption, states::*};
2use core::{
3 mem::MaybeUninit,
4 ops::{Deref, DerefMut},
5 sync::atomic::Ordering,
6};
7
8impl<T> ConcurrentOption<T> {
9 /// Converts from `Option<T>` (or `&mut Option<T>`) to `Option<&mut T::Target>`.
10 ///
11 /// Leaves the original `Option` in-place, creating a new one containing a mutable reference to
12 /// the inner type's [`Deref::Target`] type.
13 ///
14 /// # Examples
15 ///
16 /// ```rust
17 /// use orx_concurrent_option::*;
18 ///
19 /// let mut x: ConcurrentOption<String> = ConcurrentOption::some("hey".to_owned());
20 /// assert_eq!(x.exclusive_as_deref_mut().map(|x| {
21 /// x.make_ascii_uppercase();
22 /// x
23 /// }), Some("HEY".to_owned().as_mut_str()));
24 /// ```
25 pub fn exclusive_as_deref_mut(&mut self) -> Option<&mut <T as Deref>::Target>
26 where
27 T: DerefMut,
28 {
29 self.exclusive_as_mut().map(|x| x.deref_mut())
30 }
31
32 /// Converts from `&mut Option<T>` to `Option<&mut T>`.
33 ///
34 /// # Examples
35 ///
36 /// ```rust
37 /// use orx_concurrent_option::*;
38 ///
39 /// let mut x = ConcurrentOption::some(2);
40 /// match x.exclusive_as_mut() {
41 /// Some(v) => *v = 42,
42 /// None => {},
43 /// }
44 /// assert_eq!(unsafe { x.as_ref() }, Some(&42));
45 /// ```
46 pub fn exclusive_as_mut(&mut self) -> Option<&mut T> {
47 match self.state.load(Ordering::Relaxed) {
48 SOME => Some(unsafe { (*self.value.get()).assume_init_mut() }),
49 _ => None,
50 }
51 }
52
53 /// Takes the value out of the option, leaving a None in its place.
54 ///
55 /// # Examples
56 ///
57 /// ```rust
58 /// use orx_concurrent_option::*;
59 ///
60 /// let mut x = ConcurrentOption::some(42);
61 /// let y = x.exclusive_take();
62 /// assert_eq!(x, ConcurrentOption::none());
63 /// assert_eq!(y, Some(42));
64 ///
65 /// let mut x: ConcurrentOption<u32> = ConcurrentOption::none();
66 /// let y = x.exclusive_take();
67 /// assert_eq!(x, ConcurrentOption::none());
68 /// assert_eq!(y, None);
69 /// ```
70 pub fn exclusive_take(&mut self) -> Option<T> {
71 match self.state.load(Ordering::Relaxed) {
72 SOME => {
73 self.state.store(NONE, Ordering::Relaxed);
74 let x = unsafe { &mut *self.value.get() };
75 Some(unsafe { x.assume_init_read() })
76 }
77 _ => None,
78 }
79 }
80
81 /// Takes the value out of the option, but only if the predicate evaluates to
82 /// `true` on a mutable reference to the value.
83 ///
84 /// In other words, replaces `self` with None if the predicate returns `true`.
85 /// This method operates similar to [`ConcurrentOption::exclusive_take`] but conditional.
86 ///
87 /// # Examples
88 ///
89 /// ```rust
90 /// use orx_concurrent_option::*;
91 ///
92 /// let mut x = ConcurrentOption::some(42);
93 ///
94 /// let prev = x.exclusive_take_if(|v| if *v == 42 {
95 /// *v += 1;
96 /// false
97 /// } else {
98 /// false
99 /// });
100 /// assert_eq!(x, ConcurrentOption::some(43));
101 /// assert_eq!(prev, None);
102 ///
103 /// let prev = x.exclusive_take_if(|v| *v == 43);
104 /// assert_eq!(x, ConcurrentOption::none());
105 /// assert_eq!(prev, Some(43));
106 /// ```
107 pub fn exclusive_take_if<P>(&mut self, predicate: P) -> Option<T>
108 where
109 P: FnOnce(&mut T) -> bool,
110 {
111 match self.exclusive_as_mut().is_some_and(predicate) {
112 true => self.exclusive_take(),
113 false => None,
114 }
115 }
116
117 /// Returns a mutable iterator over the possibly contained value; yields
118 /// * the single element if the option is of Some variant;
119 /// * no elements otherwise.
120 ///
121 /// # Examples
122 ///
123 /// ```rust
124 /// use orx_concurrent_option::*;
125 ///
126 /// let mut x = ConcurrentOption::some(4);
127 /// match x.exclusive_iter_mut().next() {
128 /// Some(v) => *v = 42,
129 /// None => {},
130 /// }
131 /// assert_eq!(x, ConcurrentOption::some(42));
132 ///
133 /// let mut x: ConcurrentOption<u32> = ConcurrentOption::none();
134 /// assert_eq!(x.exclusive_iter_mut().next(), None);
135 /// ```
136 pub fn exclusive_iter_mut(&mut self) -> crate::iter::IterMut<'_, T> {
137 let maybe = self.exclusive_as_mut();
138 crate::iter::IterMut { maybe }
139 }
140
141 /// Replaces the actual value in the option by the value given in parameter,
142 /// returning the old value if present,
143 /// leaving a Some in its place without de-initializing either one.
144 ///
145 /// # Examples
146 ///
147 /// ```rust
148 /// use orx_concurrent_option::*;
149 ///
150 /// let mut x = ConcurrentOption::some(2);
151 /// let old = x.exclusive_replace(5);
152 /// assert_eq!(x, ConcurrentOption::some(5));
153 /// assert_eq!(old, Some(2));
154 ///
155 /// let mut x: ConcurrentOption<u32> = ConcurrentOption::none();
156 /// let old = x.exclusive_replace(3);
157 /// assert_eq!(x, ConcurrentOption::some(3));
158 /// assert_eq!(old, None);
159 /// ```
160 #[allow(clippy::panic, clippy::missing_panics_doc)]
161 pub fn exclusive_replace(&mut self, value: T) -> Option<T> {
162 match self.state.load(Ordering::Relaxed) {
163 SOME => {
164 self.state.store(RESERVED, Ordering::Relaxed);
165 let x = unsafe { (*self.value.get()).assume_init_mut() };
166 let old = core::mem::replace(x, value);
167 self.state.store(SOME, Ordering::Relaxed);
168 Some(old)
169 }
170 NONE => {
171 self.state.store(RESERVED, Ordering::Relaxed);
172 self.value = MaybeUninit::new(value).into();
173 self.state.store(SOME, Ordering::Relaxed);
174 None
175 }
176 _ => panic!("ConcurrentOption value is `replace`d while its value is being written."),
177 }
178 }
179
180 /// Inserts `value` into the option, then returns a mutable reference to it.
181 ///
182 /// If the option already contains a value, the old value is dropped.
183 ///
184 /// See also [`Option::get_or_insert`], which doesn't update the value if
185 /// the option already contains Some.
186 ///
187 /// # Examples
188 ///
189 /// ```rust
190 /// use orx_concurrent_option::*;
191 ///
192 /// let mut opt: ConcurrentOption<_> = ConcurrentOption::none();
193 ///
194 /// let val = opt.exclusive_insert(1);
195 /// assert_eq!(*val, 1);
196 /// assert_eq!(unsafe { opt.as_ref() }, Some(&1));
197 ///
198 /// let val = opt.exclusive_insert(2);
199 /// assert_eq!(*val, 2);
200 /// *val = 3;
201 /// assert_eq!(opt.unwrap(), 3);
202 /// ```
203 #[allow(clippy::panic, clippy::missing_panics_doc)]
204 pub fn exclusive_insert(&mut self, value: T) -> &mut T {
205 match self.state.load(Ordering::Relaxed) {
206 SOME => {
207 self.state.store(RESERVED, Ordering::Relaxed);
208 let x = unsafe { (*self.value.get()).assume_init_mut() };
209 let _ = core::mem::replace(x, value);
210 self.state.store(SOME, Ordering::Relaxed);
211 }
212 NONE => {
213 self.state.store(RESERVED, Ordering::Relaxed);
214 self.value = MaybeUninit::new(value).into();
215 self.state.store(SOME, Ordering::Relaxed);
216 }
217 _ => panic!("ConcurrentOption value is `insert`ed while its value is being written."),
218 }
219
220 self.exclusive_as_mut().expect("should be some")
221 }
222
223 /// Inserts `value` into the option if it is None, then
224 /// returns a mutable reference to the contained value.
225 ///
226 /// See also [`ConcurrentOption::insert`], which updates the value even if
227 /// the option already contains Some.
228 ///
229 /// # Examples
230 ///
231 /// ```rust
232 /// use orx_concurrent_option::*;
233 ///
234 /// let mut x = ConcurrentOption::none();
235 ///
236 /// {
237 /// let y: &mut u32 = x.exclusive_get_or_insert(5);
238 /// assert_eq!(y, &5);
239 ///
240 /// *y = 7;
241 /// }
242 ///
243 /// assert_eq!(x, ConcurrentOption::some(7));
244 /// ```
245 pub fn exclusive_get_or_insert(&mut self, value: T) -> &mut T {
246 self.exclusive_get_or_insert_with(|| value)
247 }
248
249 /// Inserts a value computed from `f` into the option if it is None,
250 /// then returns a mutable reference to the contained value.
251 ///
252 /// # Examples
253 ///
254 /// ```rust
255 /// use orx_concurrent_option::*;
256 ///
257 /// let mut x = ConcurrentOption::none();
258 ///
259 /// {
260 /// let y: &mut u32 = x.exclusive_get_or_insert_with(|| 5);
261 /// assert_eq!(y, &5);
262 ///
263 /// *y = 7;
264 /// }
265 ///
266 /// assert_eq!(x, ConcurrentOption::some(7));
267 /// ```
268 #[allow(clippy::panic, clippy::missing_panics_doc)]
269 pub fn exclusive_get_or_insert_with<F>(&mut self, f: F) -> &mut T
270 where
271 F: FnOnce() -> T,
272 {
273 match self.state.load(Ordering::Relaxed) {
274 SOME => self.exclusive_as_mut().expect("is guaranteed to be some"),
275 NONE => {
276 self.state.store(RESERVED, Ordering::Relaxed);
277 self.value = MaybeUninit::new(f()).into();
278 self.state.store(SOME, Ordering::Relaxed);
279 self.exclusive_as_mut().expect("is guaranteed to be some")
280 }
281 _ => panic!(
282 "ConcurrentOption `get_or_insert_with` is called while its value is being written."
283 ),
284 }
285 }
286}