mycelium_util/mem/
maybe_uninit.rs

1#![allow(dead_code)] // most of this isn't used yet...
2use core::{fmt, mem::MaybeUninit};
3
4/// A checked version of [`core::mem::MaybeUninit`].
5///
6/// This is similar to [`core::mem::MaybeUninit`] in release builds. In debug
7/// mode builds, it additionally stores a flag tracking whether the value is
8/// initialized, and asserts that the cell is initialized when it is accessed.
9///
10/// # Differences from `MaybeUninit`
11///
12/// This type is **not** capable of tracking initialization of
13/// partially-initialized values, so it lacks `core::mem::MaybeUninit`'s array
14/// and slice methods. Additionally, it does not implement a version of
15/// [`MaybeUninit::zeroed`], because it does not know whether a zeroed `T` is
16/// valid or not.
17pub struct CheckedMaybeUninit<T> {
18    value: MaybeUninit<T>,
19    #[cfg(debug_assertions)]
20    initialized: bool,
21}
22
23impl<T> CheckedMaybeUninit<T> {
24    /// Creates a new `CheckedMaybeUninit<T>` initialized with the given value.
25    /// It is safe to call [`assume_init`] on the return value of this function.
26    ///
27    /// Note that dropping a `CheckedMaybeUninit<T>` will never call `T`'s drop code.
28    /// It is your responsibility to make sure `T` gets dropped if it got initialized.
29    ///
30    /// [`assume_init`]: Self::assume_init
31    #[must_use = "use `forget` to avoid running Drop code"]
32    #[inline(always)]
33    pub const fn new(val: T) -> Self {
34        Self {
35            value: MaybeUninit::new(val),
36            #[cfg(debug_assertions)]
37            initialized: true,
38        }
39    }
40
41    /// Creates a new `CheckedMaybeUninit<T>` in an uninitialized state.
42    ///
43    /// Note that dropping a `CheckedMaybeUninit<T>` will never call `T`'s drop code.
44    /// It is your responsibility to make sure `T` gets dropped if it got initialized.
45    ///
46    /// See the [type-level documentation][CheckedMaybeUninit] for some examples.
47    #[must_use]
48    #[inline(always)]
49    pub const fn uninit() -> Self {
50        Self {
51            value: MaybeUninit::uninit(),
52            #[cfg(debug_assertions)]
53            initialized: false,
54        }
55    }
56
57    /// Sets the value of the `CheckedMaybeUninit<T>`.
58    ///
59    /// This overwrites any previous value without dropping it, so be careful
60    /// not to use this twice unless you want to skip running the destructor.
61    /// For your convenience, this also returns a mutable reference to the
62    /// (now safely initialized) contents of `self`.
63    ///
64    /// As the content is stored inside a `CheckedMaybeUninit`, the destructor is not
65    /// run for the inner data if the MaybeUninit leaves scope without a call to
66    /// [`assume_init`], [`assume_init_drop`], or similar. Code that receives
67    /// the mutable reference returned by this function needs to keep this in
68    /// mind. The safety model of Rust regards leaks as safe, but they are
69    /// usually still undesirable. This being said, the mutable reference
70    /// behaves like any other mutable reference would, so assigning a new value
71    /// to it will drop the old content.
72    ///
73    /// [`assume_init`]: Self::assume_init
74    /// [`assume_init_drop`]: Self::assume_init_drop
75    #[inline(always)]
76    pub fn write(&mut self, val: T) -> &mut T {
77        self.init().write(val)
78    }
79
80    /// Gets a pointer to the contained value. Reading from this pointer or turning it
81    /// into a reference is undefined behavior unless the `CheckedMaybeUninit<T>` is initialized.
82    /// Writing to memory that this pointer (non-transitively) points to is undefined behavior
83    /// (except inside an `UnsafeCell<T>`).
84    #[inline(always)]
85    #[track_caller]
86    pub fn as_ptr(&self) -> *const T {
87        self.assert_init("as_ptr").as_ptr()
88    }
89
90    /// Gets a mutable pointer to the contained value. Reading from this pointer or turning it
91    /// into a reference is undefined behavior unless the `CheckedMaybeUninit<T>` is initialized.
92    #[inline(always)]
93    #[track_caller]
94    pub fn as_mut_ptr(&mut self) -> *mut T {
95        self.assert_init_mut("as_mut_ptr").as_mut_ptr()
96    }
97
98    /// Extracts the value from the `CheckedMaybeUninit<T>` container. This is a great way
99    /// to ensure that the data will get dropped, because the resulting `T` is
100    /// subject to the usual drop handling.
101    ///
102    /// # Safety
103    ///
104    /// It is up to the caller to guarantee that the `CheckedMaybeUninit<T>` really is in an initialized
105    /// state. Calling this when the content is not yet fully initialized causes immediate undefined
106    /// behavior. The [type-level documentation][inv] contains more information about
107    /// this initialization invariant.
108    ///
109    /// [inv]: #initialization-invariant
110    ///
111    /// On top of that, remember that most types have additional invariants beyond merely
112    /// being considered initialized at the type level. For example, a `1`-initialized [`Vec<T>`]
113    /// is considered initialized (under the current implementation; this does not constitute
114    /// a stable guarantee) because the only requirement the compiler knows about it
115    /// is that the data pointer must be non-null. Creating such a `Vec<T>` does not cause
116    /// *immediate* undefined behavior, but will cause undefined behavior with most
117    /// safe operations (including dropping it).
118    ///
119    /// [`Vec<T>`]: ../../std/vec/struct.Vec.html
120    ///
121    /// # Examples
122    ///
123    /// Correct usage of this method:
124    ///
125    /// ```rust
126    /// use std::mem::MaybeUninit;
127    ///
128    /// let mut x = MaybeUninit::<bool>::uninit();
129    /// x.write(true);
130    /// let x_init = unsafe { x.assume_init() };
131    /// assert_eq!(x_init, true);
132    /// ```
133    ///
134    /// *Incorrect* usage of this method:
135    ///
136    /// ```rust,no_run
137    /// use std::mem::MaybeUninit;
138    ///
139    /// let x = MaybeUninit::<Vec<u32>>::uninit();
140    /// let x_init = unsafe { x.assume_init() };
141    /// // `x` had not been initialized yet, so this last line caused undefined behavior. ⚠️
142    /// ```
143    #[inline(always)]
144    #[track_caller]
145    pub unsafe fn assume_init(self) -> T {
146        self.assert_init_val("assume_init").assume_init()
147    }
148
149    /// Reads the value from the `CheckedMaybeUninit<T>` container. The resulting `T` is subject
150    /// to the usual drop handling.
151    ///
152    /// Whenever possible, it is preferable to use [`assume_init`] instead, which
153    /// prevents duplicating the content of the `CheckedMaybeUninit<T>`.
154    ///
155    /// # Safety
156    ///
157    /// It is up to the caller to guarantee that the `CheckedMaybeUninit<T>` really is in an initialized
158    /// state. Calling this when the content is not yet fully initialized causes undefined
159    /// behavior. The [type-level documentation][inv] contains more information about
160    /// this initialization invariant.
161    ///
162    /// Moreover, similar to the [`ptr::read`] function, this function creates a
163    /// bitwise copy of the contents, regardless whether the contained type
164    /// implements the [`Copy`] trait or not. When using multiple copies of the
165    /// data (by calling `assume_init_read` multiple times, or first calling
166    /// `assume_init_read` and then [`assume_init`]), it is your responsibility
167    /// to ensure that that data may indeed be duplicated.
168    ///
169    /// [inv]: #initialization-invariant
170    /// [`assume_init`]: MaybeUninit::assume_init
171    /// [`ptr::read`]: core::ptr::read
172    #[inline(always)]
173    #[track_caller]
174    pub unsafe fn assume_init_read(&self) -> T {
175        self.assert_init("assume_init_read").assume_init_read()
176    }
177
178    /// Drops the contained value in place.
179    ///
180    /// If you have ownership of the `CheckedMaybeUninit`, you can also use
181    /// [`assume_init`] as an alternative.
182    ///
183    /// # Safety
184    ///
185    /// It is up to the caller to guarantee that the `CheckedMaybeUninit<T>` really is
186    /// in an initialized state. Calling this when the content is not yet fully
187    /// initialized causes undefined behavior.
188    ///
189    /// On top of that, all additional invariants of the type `T` must be
190    /// satisfied, as the `Drop` implementation of `T` (or its members) may
191    /// rely on this. For example, setting a [`Vec<T>`] to an invalid but
192    /// non-null address makes it initialized (under the current implementation;
193    /// this does not constitute a stable guarantee), because the only
194    /// requirement the compiler knows about it is that the data pointer must be
195    /// non-null. Dropping such a `Vec<T>` however will cause undefined
196    /// behaviour.
197    ///
198    /// [`assume_init`]: MaybeUninit::assume_init
199    #[inline(always)]
200    #[track_caller]
201    pub unsafe fn assume_init_drop(&mut self) {
202        self.assert_init_mut("assume_init_drop").assume_init_drop()
203    }
204
205    /// Gets a shared reference to the contained value.
206    ///
207    /// This can be useful when we want to access a `CheckedMaybeUninit` that has been
208    /// initialized but don't have ownership of the `CheckedMaybeUninit` (preventing the use
209    /// of `.assume_init()`).
210    ///
211    /// # Safety
212    ///
213    /// Calling this when the content is not yet fully initialized causes undefined
214    /// behavior: it is up to the caller to guarantee that the `CheckedMaybeUninit<T>` really
215    /// is in an initialized state.
216    ///
217    /// # Examples
218    ///
219    /// ### Correct usage of this method:
220    ///
221    /// ```rust
222    /// use std::mem::MaybeUninit;
223    ///
224    /// let mut x = MaybeUninit::<Vec<u32>>::uninit();
225    /// // Initialize `x`:
226    /// x.write(vec![1, 2, 3]);
227    /// // Now that our `CheckedMaybeUninit<_>` is known to be initialized, it is okay to
228    /// // create a shared reference to it:
229    /// let x: &Vec<u32> = unsafe {
230    ///     // SAFETY: `x` has been initialized.
231    ///     x.assume_init_ref()
232    /// };
233    /// assert_eq!(x, &vec![1, 2, 3]);
234    /// ```
235    ///
236    /// ### *Incorrect* usages of this method:
237    ///
238    /// ```rust,no_run
239    /// use std::mem::MaybeUninit;
240    ///
241    /// let x = MaybeUninit::<Vec<u32>>::uninit();
242    /// let x_vec: &Vec<u32> = unsafe { x.assume_init_ref() };
243    /// // We have created a reference to an uninitialized vector! This is undefined behavior. ⚠️
244    /// ```
245    ///
246    /// ```rust,no_run
247    /// use std::{cell::Cell, mem::MaybeUninit};
248    ///
249    /// let b = MaybeUninit::<Cell<bool>>::uninit();
250    /// // Initialize the `CheckedMaybeUninit` using `Cell::set`:
251    /// unsafe {
252    ///     b.assume_init_ref().set(true);
253    ///    // ^^^^^^^^^^^^^^^
254    ///    // Reference to an uninitialized `Cell<bool>`: UB!
255    /// }
256    /// ```
257    #[track_caller]
258    #[inline(always)]
259    pub unsafe fn assume_init_ref(&self) -> &T {
260        self.assert_init("assume_init_ref").assume_init_ref()
261    }
262
263    /// Gets a mutable (unique) reference to the contained value.
264    ///
265    /// This can be useful when we want to access a `CheckedMaybeUninit` that has been
266    /// initialized but don't have ownership of the `CheckedMaybeUninit` (preventing the use
267    /// of `.assume_init()`).
268    ///
269    /// # Safety
270    ///
271    /// Calling this when the content is not yet fully initialized causes undefined
272    /// behavior: it is up to the caller to guarantee that the `CheckedMaybeUninit<T>` really
273    /// is in an initialized state. For instance, `.assume_init_mut()` cannot be used to
274    /// initialize a `CheckedMaybeUninit`.
275    ///
276    /// # Examples
277    ///
278    /// ### Correct usage of this method:
279    ///
280    /// ```rust
281    /// # #![allow(unexpected_cfgs)]
282    /// use std::mem::MaybeUninit;
283    ///
284    /// # unsafe extern "C" fn initialize_buffer(buf: *mut [u8; 1024]) { *buf = [0; 1024] }
285    /// # #[cfg(FALSE)]
286    /// extern "C" {
287    ///     /// Initializes *all* the bytes of the input buffer.
288    ///     fn initialize_buffer(buf: *mut [u8; 1024]);
289    /// }
290    ///
291    /// let mut buf = MaybeUninit::<[u8; 1024]>::uninit();
292    ///
293    /// // Initialize `buf`:
294    /// unsafe { initialize_buffer(buf.as_mut_ptr()); }
295    /// // Now we know that `buf` has been initialized, so we could `.assume_init()` it.
296    /// // However, using `.assume_init()` may trigger a `memcpy` of the 1024 bytes.
297    /// // To assert our buffer has been initialized without copying it, we upgrade
298    /// // the `&mut MaybeUninit<[u8; 1024]>` to a `&mut [u8; 1024]`:
299    /// let buf: &mut [u8; 1024] = unsafe {
300    ///     // SAFETY: `buf` has been initialized.
301    ///     buf.assume_init_mut()
302    /// };
303    ///
304    /// // Now we can use `buf` as a normal slice:
305    /// buf.sort_unstable();
306    /// debug_assert!(
307    ///     buf.windows(2).all(|pair| pair[0] <= pair[1]),
308    ///     "buffer is sorted",
309    /// );
310    /// ```
311    ///
312    /// ### *Incorrect* usages of this method:
313    ///
314    /// You cannot use `.assume_init_mut()` to initialize a value:
315    ///
316    /// ```rust,no_run
317    /// use std::mem::MaybeUninit;
318    ///
319    /// let mut b = MaybeUninit::<bool>::uninit();
320    /// unsafe {
321    ///     *b.assume_init_mut() = true;
322    ///     // We have created a (mutable) reference to an uninitialized `bool`!
323    ///     // This is undefined behavior. ⚠️
324    /// }
325    /// ```
326    ///
327    /// For instance, you cannot [`Read`] into an uninitialized buffer:
328    ///
329    /// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
330    ///
331    /// ```rust,no_run
332    /// use std::{io, mem::MaybeUninit};
333    ///
334    /// fn read_chunk (reader: &'_ mut dyn io::Read) -> io::Result<[u8; 64]>
335    /// {
336    ///     let mut buffer = MaybeUninit::<[u8; 64]>::uninit();
337    ///     reader.read_exact(unsafe { buffer.assume_init_mut() })?;
338    ///                             // ^^^^^^^^^^^^^^^^^^^^^^^^
339    ///                             // (mutable) reference to uninitialized memory!
340    ///                             // This is undefined behavior.
341    ///     Ok(unsafe { buffer.assume_init() })
342    /// }
343    /// ```
344    ///
345    /// Nor can you use direct field access to do field-by-field gradual initialization:
346    ///
347    /// ```rust,no_run
348    /// use std::{mem::MaybeUninit, ptr};
349    ///
350    /// struct Foo {
351    ///     a: u32,
352    ///     b: u8,
353    /// }
354    ///
355    /// let foo: Foo = unsafe {
356    ///     let mut foo = MaybeUninit::<Foo>::uninit();
357    ///     ptr::write(&mut foo.assume_init_mut().a as *mut u32, 1337);
358    ///                  // ^^^^^^^^^^^^^^^^^^^^^
359    ///                  // (mutable) reference to uninitialized memory!
360    ///                  // This is undefined behavior.
361    ///     ptr::write(&mut foo.assume_init_mut().b as *mut u8, 42);
362    ///                  // ^^^^^^^^^^^^^^^^^^^^^
363    ///                  // (mutable) reference to uninitialized memory!
364    ///                  // This is undefined behavior.
365    ///     foo.assume_init()
366    /// };
367    /// ```
368    #[inline(always)]
369    #[track_caller]
370    pub unsafe fn assume_init_mut(&mut self) -> &mut T {
371        self.assert_init_mut("assume_init_mut").assume_init_mut()
372    }
373
374    #[inline(always)]
375    fn init(&mut self) -> &mut MaybeUninit<T> {
376        #[cfg(debug_assertions)]
377        {
378            self.initialized = true;
379        }
380        &mut self.value
381    }
382
383    #[inline(always)]
384    #[track_caller]
385    fn assert_init(&self, _method: &'static str) -> &MaybeUninit<T> {
386        #[cfg(debug_assertions)]
387        debug_assert!(
388            self.initialized,
389            "`MaybeUninit::{_method}` called on a `MaybeUninit` cell that was not initialized! this is a bug!",
390        );
391        &self.value
392    }
393
394    #[inline(always)]
395    #[track_caller]
396    fn assert_init_mut(&mut self, _method: &'static str) -> &mut MaybeUninit<T> {
397        #[cfg(debug_assertions)]
398        debug_assert!(
399            self.initialized,
400            "`MaybeUninit::{_method}` called on a `MaybeUninit` cell that was not initialized! this is a bug!",
401        );
402        &mut self.value
403    }
404
405    #[inline(always)]
406    #[track_caller]
407    fn assert_init_val(self, _method: &'static str) -> MaybeUninit<T> {
408        #[cfg(debug_assertions)]
409        debug_assert!(
410            self.initialized,
411            "`MaybeUninit::{_method}` called on a `MaybeUninit` cell that was not initialized! this is a bug!",
412        );
413        self.value
414    }
415}
416
417impl<T: fmt::Debug> fmt::Debug for CheckedMaybeUninit<T> {
418    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
419        let mut s = f.debug_struct("CheckedMaybeUninit");
420        #[cfg(debug_assertions)]
421        if self.initialized {
422            s.field("value", unsafe { &self.assume_init_ref() });
423        } else {
424            s.field("value", &format_args!("<uninitialized>"));
425        }
426
427        #[cfg(not(test))]
428        {
429            s.field("value", &format_args!("<maybe uninitialized>"));
430        }
431
432        s.finish()
433    }
434}