Skip to main content

rs_matter/utils/
maybe.rs

1/*
2 *
3 *    Copyright (c) 2024-2025 Project CHIP Authors
4 *
5 *    Licensed under the Apache License, Version 2.0 (the "License");
6 *    you may not use this file except in compliance with the License.
7 *    You may obtain a copy of the License at
8 *
9 *        http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *    Unless required by applicable law or agreed to in writing, software
12 *    distributed under the License is distributed on an "AS IS" BASIS,
13 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *    See the License for the specific language governing permissions and
15 *    limitations under the License.
16 */
17
18use core::fmt::Debug;
19use core::hash::Hash;
20use core::marker::PhantomData;
21use core::mem::MaybeUninit;
22use core::ops::{Deref, DerefMut};
23use core::ptr::addr_of_mut;
24
25use super::init;
26
27/// Represents a type similar in spirit to the built-in `Option` type.
28/// Unlike `Option` however, `Maybe` _does_ have in-place initializer support.
29///
30/// (In-place initializer support is impossible to provide for `Option` due to its
31/// enum nature, and because it is not marked with `repr(transparent)`).
32///
33/// `Maybe` is convertable to and from `Option` (via the `From` / `Into` traits),
34/// however these conversions are not recommended when the wrapped value is large
35/// which defeats the purpose of using `Maybe` in the first place.
36///
37/// The canonical way to use `Maybe` with large values is to initialize it in-place with
38/// one of the provided init constructors, and then use one of the `as_ref`, `as_mut`,
39/// `as_deref` and `as_deref_mut` methods to access the wrapped value.
40#[derive(Debug)]
41pub struct Maybe<T, G = ()> {
42    some: bool,
43    value: MaybeUninit<T>,
44    _tag: PhantomData<G>,
45}
46
47impl<T, G> Maybe<T, G> {
48    /// Create a new `Maybe` value from an `Option`.
49    ///
50    /// Note that when the wrapped value is large, it is recommended instead to use
51    /// `Maybe::init_none()` and `Maybe::init_some()` to create the `Maybe` value in-place.
52    pub fn new(value: Option<T>) -> Self {
53        match value {
54            Some(v) => Self::some(v),
55            None => Self::none(),
56        }
57    }
58
59    /// Create a new, empty `Maybe` value.
60    pub const fn none() -> Self {
61        Self {
62            some: false,
63            value: MaybeUninit::uninit(),
64            _tag: PhantomData,
65        }
66    }
67
68    /// Create a new `Maybe` value with a wrapped value.
69    pub const fn some(value: T) -> Self {
70        Self {
71            some: true,
72            value: MaybeUninit::new(value),
73            _tag: PhantomData,
74        }
75    }
76
77    /// Create an in-place initializer for a `Maybe` value that is empty.
78    pub fn init_none() -> impl init::Init<Self> {
79        unsafe {
80            init::init_from_closure(move |slot: *mut Self| {
81                addr_of_mut!((*slot).some).write(false);
82
83                Ok(())
84            })
85        }
86    }
87
88    /// Create an in-place initializer for a `Maybe` value that is not empty
89    /// by initializing the wrapped value with the provided initializer.
90    pub fn init_some<I: init::Init<T, E>, E>(value: I) -> impl init::Init<Self, E> {
91        Self::init(Some(value))
92    }
93
94    /// Create an in-place initializer for a `Maybe` value that might or might
95    /// not be empty.
96    pub fn init<I: init::Init<T, E>, E>(value: Option<I>) -> impl init::Init<Self, E> {
97        unsafe {
98            init::init_from_closure(move |slot: *mut Self| {
99                let some = value.is_some();
100
101                if let Some(value) = value {
102                    value.__init(addr_of_mut!((*slot).value) as _)?;
103                }
104
105                // Only set this once the value is initialized
106                addr_of_mut!((*slot).some).write(some);
107
108                Ok(())
109            })
110        }
111    }
112
113    /// Sets the `Maybe` value to "none".
114    pub fn clear(&mut self) {
115        if self.some {
116            unsafe {
117                let slot = addr_of_mut!(*self);
118
119                addr_of_mut!((*slot).some).write(false);
120
121                let value = addr_of_mut!((*slot).value) as *mut T;
122
123                core::ptr::drop_in_place(value);
124            }
125        }
126    }
127
128    /// Re-initialize the `Maybe` value with a new in-place initializer.
129    pub fn reinit<I: init::Init<Self>>(&mut self, value: I) {
130        // Unwrap is safe because the initializer is infallible
131        unwrap!(Self::try_reinit(self, value));
132    }
133
134    /// Try to re-initialize the `Maybe` value with a new in-place initializer.
135    ///
136    /// If the re-initialization fails, the `Maybe` value is left to `none`.
137    pub fn try_reinit<I: init::Init<Self, E>, E>(&mut self, value: I) -> Result<(), E> {
138        self.clear();
139
140        unsafe {
141            let slot = addr_of_mut!(*self);
142
143            value.__init(slot)
144        }
145    }
146
147    /// Return a mutable reference to the wrapped value, if it exists.
148    pub fn as_mut(&mut self) -> Maybe<&mut T, G> {
149        if self.some {
150            Maybe::some(unsafe { self.value.assume_init_mut() })
151        } else {
152            Maybe::none()
153        }
154    }
155
156    /// Return a reference to the wrapped value, if it exists.
157    pub fn as_ref(&self) -> Maybe<&T, G> {
158        if self.some {
159            Maybe::some(unsafe { self.value.assume_init_ref() })
160        } else {
161            Maybe::none()
162        }
163    }
164
165    /// Return - as an `Option` - a mutable reference to the wrapped value, if it exists.
166    pub fn as_opt_mut(&mut self) -> Option<&mut T> {
167        if self.some {
168            Some(unsafe { self.value.assume_init_mut() })
169        } else {
170            None
171        }
172    }
173
174    /// Return - as an `Option` - a reference to the wrapped value, if it exists.
175    pub fn as_opt_ref(&self) -> Option<&T> {
176        if self.some {
177            Some(unsafe { self.value.assume_init_ref() })
178        } else {
179            None
180        }
181    }
182
183    /// Derefs the wrapped value, if it exists.
184    pub fn as_deref(&self) -> Maybe<&T::Target, G>
185    where
186        T: Deref,
187    {
188        match self.as_opt_ref() {
189            Some(t) => Maybe::some(t.deref()),
190            None => Maybe::none(),
191        }
192    }
193
194    /// Derefs mutably the wrapped value, if it exists.
195    pub fn as_deref_mut(&mut self) -> Maybe<&mut T::Target, G>
196    where
197        T: DerefMut,
198    {
199        match self.as_opt_mut() {
200            Some(t) => Maybe::some(t.deref_mut()),
201            None => Maybe::none(),
202        }
203    }
204
205    /// Derefs - as an `Option` - the wrapped value, if it exists.
206    pub fn as_opt_deref(&self) -> Option<&T::Target>
207    where
208        T: Deref,
209    {
210        match self.as_opt_ref() {
211            Some(t) => Some(t.deref()),
212            None => None,
213        }
214    }
215
216    /// Derefs - as an `Option` - mutably the wrapped value, if it exists.
217    pub fn as_opt_deref_mut(&mut self) -> Option<&mut T::Target>
218    where
219        T: DerefMut,
220    {
221        match self.as_opt_mut() {
222            Some(t) => Some(t.deref_mut()),
223            None => None,
224        }
225    }
226
227    /// Consume the `Maybe` value and return the wrapped value, if it exists.
228    ///
229    /// Note that this method is not efficient when the wrapped value is large
230    /// (might result in big stack memory usage due to moves), hence its usage
231    /// is not recommended when the wrapped value is large.
232    pub fn into_option(mut self) -> Option<T> {
233        if !self.some {
234            return None;
235        }
236
237        Some(unsafe {
238            let slot = addr_of_mut!(self);
239
240            let ret = core::ptr::read(addr_of_mut!((*slot).value) as *mut _);
241
242            // So that `T` is not double-dropped on dtor
243            self.some = false;
244
245            ret
246        })
247    }
248
249    /// Return whether the `Maybe` value is empty.
250    pub fn is_none(&self) -> bool {
251        !self.some
252    }
253
254    /// Return whether the `Maybe` value is not empty.
255    pub fn is_some(&self) -> bool {
256        self.some
257    }
258}
259
260impl<T, G> Drop for Maybe<T, G> {
261    fn drop(&mut self) {
262        // Explicit drop to ensure that the wrapped value is dropped
263        // The compiler won't drop it automatically, because it is tracked as `MaybeUninit<T>`
264        // (even if it is initialized in the meantime, i.e. `self.some == true`)
265        self.clear();
266    }
267}
268
269impl<T, G> Default for Maybe<T, G> {
270    fn default() -> Self {
271        Self::none()
272    }
273}
274
275impl<T, G> From<Option<T>> for Maybe<T, G> {
276    fn from(value: Option<T>) -> Self {
277        Self::new(value)
278    }
279}
280
281impl<T, G> From<Maybe<T, G>> for Option<T> {
282    fn from(value: Maybe<T, G>) -> Self {
283        value.into_option()
284    }
285}
286
287impl<T, G> Clone for Maybe<T, G>
288where
289    T: Clone,
290{
291    fn clone(&self) -> Self {
292        Maybe::<_, G>::new(self.as_opt_ref().cloned())
293    }
294}
295
296impl<T, G> PartialEq for Maybe<T, G>
297where
298    T: PartialEq,
299{
300    fn eq(&self, other: &Self) -> bool {
301        self.as_opt_ref() == other.as_opt_ref()
302    }
303}
304
305impl<T, G> Eq for Maybe<T, G> where T: Eq {}
306
307impl<T, G> Hash for Maybe<T, G>
308where
309    T: Hash,
310{
311    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
312        self.as_opt_ref().hash(state)
313    }
314}
315
316#[cfg(feature = "defmt")]
317impl<T, G> defmt::Format for Maybe<T, G>
318where
319    T: defmt::Format,
320{
321    fn format(&self, f: defmt::Formatter<'_>) {
322        if self.is_none() {
323            defmt::write!(f, "None")
324        } else {
325            defmt::write!(f, "Some({})", unsafe { self.value.assume_init_ref() })
326        }
327    }
328}
329
330#[cfg(test)]
331mod tests {
332    use super::Maybe;
333
334    macro_rules! droppable {
335        () => {
336            static COUNT: core::sync::atomic::AtomicI32 = core::sync::atomic::AtomicI32::new(0);
337
338            #[derive(Eq, Ord, PartialEq, PartialOrd)]
339            struct Droppable(());
340
341            impl Droppable {
342                fn new() -> Self {
343                    COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
344                    Droppable(())
345                }
346
347                fn count() -> i32 {
348                    COUNT.load(core::sync::atomic::Ordering::Relaxed)
349                }
350            }
351
352            impl Drop for Droppable {
353                fn drop(&mut self) {
354                    COUNT.fetch_sub(1, core::sync::atomic::Ordering::Relaxed);
355                }
356            }
357
358            impl Clone for Droppable {
359                fn clone(&self) -> Self {
360                    COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
361
362                    Self(())
363                }
364            }
365        };
366    }
367
368    #[test]
369    fn drop() {
370        droppable!();
371
372        // Test dropping none
373
374        assert_eq!(Droppable::count(), 0);
375
376        {
377            let _m: Maybe<Droppable> = Maybe::none();
378        }
379
380        assert_eq!(Droppable::count(), 0);
381
382        // Test dropping some
383
384        {
385            let _m: Maybe<Droppable> = Maybe::some(Droppable::new());
386        }
387
388        assert_eq!(Droppable::count(), 0);
389
390        // Test `into_option` destructuring
391        {
392            let m: Maybe<Droppable> = Maybe::some(Droppable::new());
393            m.into_option();
394        }
395
396        assert_eq!(Droppable::count(), 0);
397
398        // Test clone semantics w.r.t. drop
399
400        {
401            let m: Maybe<Droppable> = Maybe::some(Droppable::new());
402
403            let _m2 = m.clone();
404
405            core::mem::drop(m);
406
407            assert_eq!(Droppable::count(), 1);
408        }
409
410        assert_eq!(Droppable::count(), 0);
411
412        // Test clear semantics w.r.t. drop
413
414        {
415            let mut m: Maybe<Droppable> = Maybe::some(Droppable::new());
416
417            m.clear();
418        }
419
420        assert_eq!(Droppable::count(), 0);
421    }
422}