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}