core/mem/maybe_dangling.rs
1#![unstable(feature = "maybe_dangling", issue = "118166")]
2
3use crate::{mem, ptr};
4
5/// Allows wrapped [references] and [boxes] to dangle.
6///
7/// That is, if a reference (or a `Box`) is wrapped in `MaybeDangling` (including when in a
8/// (nested) field of a compound type wrapped in `MaybeDangling`), it does not have to follow
9/// pointer aliasing rules or be dereferenceable.
10///
11/// This can be useful when the value can become dangling while the function holding it is still
12/// executing (particularly in concurrent code). As a somewhat absurd example, consider this code:
13///
14/// ```rust,no_run
15/// #![feature(box_as_ptr)]
16/// # use std::alloc::{dealloc, Layout};
17/// # use std::mem;
18///
19/// let mut boxed = Box::new(0_u32);
20/// let ptr = Box::as_mut_ptr(&mut boxed);
21///
22/// // Safety: the pointer comes from a box and thus was allocated before; `box` is not used afterwards
23/// unsafe { dealloc(ptr.cast(), Layout::new::<u32>()) };
24///
25/// mem::forget(boxed); // <-- this is UB!
26/// ```
27///
28/// Even though the `Box`'s destructor is not run (and thus we don't have a double free bug), this
29/// code is still UB. This is because when moving `boxed` into `forget`, its validity invariants
30/// are asserted, causing UB since the `Box` is dangling. The safety comment is as such wrong, as
31/// moving the `boxed` variable as part of the `forget` call *is* a use.
32///
33/// To fix this we could use `MaybeDangling`:
34///
35// FIXME: remove `no_run` once the semantics are actually implemented
36/// ```rust,no_run
37/// #![feature(maybe_dangling, box_as_ptr)]
38/// # use std::alloc::{dealloc, Layout};
39/// # use std::mem::{self, MaybeDangling};
40///
41/// let mut boxed = MaybeDangling::new(Box::new(0_u32));
42/// let ptr = Box::as_mut_ptr(boxed.as_mut());
43///
44/// // Safety: the pointer comes from a box and thus was allocated before; `box` is not used afterwards
45/// unsafe { dealloc(ptr.cast(), Layout::new::<u32>()) };
46///
47/// mem::forget(boxed); // <-- this is OK!
48/// ```
49///
50/// Note that the bit pattern must still be valid for the wrapped type. That is, [references]
51/// (and [boxes]) still must be aligned and non-null.
52///
53/// Additionally note that safe code can still assume that the inner value in a `MaybeDangling` is
54/// **not** dangling -- functions like [`as_ref`] and [`into_inner`] are safe. It is not sound to
55/// return a dangling reference in a `MaybeDangling` to safe code. However, it *is* sound
56/// to hold such values internally inside your code -- and there's no way to do that without
57/// this type. Note that other types can use this type and thus get the same effect; in particular,
58/// [`ManuallyDrop`] will use `MaybeDangling`.
59///
60/// Note that `MaybeDangling` doesn't prevent drops from being run, which can lead to UB if the
61/// drop observes a dangling value. If you need to prevent drops from being run use [`ManuallyDrop`]
62/// instead.
63///
64/// [references]: prim@reference
65/// [boxes]: ../../std/boxed/struct.Box.html
66/// [`into_inner`]: MaybeDangling::into_inner
67/// [`as_ref`]: MaybeDangling::as_ref
68/// [`ManuallyDrop`]: crate::mem::ManuallyDrop
69#[repr(transparent)]
70#[rustc_pub_transparent]
71#[derive(Debug, Copy, Clone, Default)]
72#[lang = "maybe_dangling"]
73pub struct MaybeDangling<P: ?Sized>(P);
74
75impl<P: ?Sized> MaybeDangling<P> {
76 /// Wraps a value in a `MaybeDangling`, allowing it to dangle.
77 pub const fn new(x: P) -> Self
78 where
79 P: Sized,
80 {
81 MaybeDangling(x)
82 }
83
84 /// Returns a reference to the inner value.
85 ///
86 /// Note that this is UB if the inner value is currently dangling.
87 pub const fn as_ref(&self) -> &P {
88 &self.0
89 }
90
91 /// Returns a mutable reference to the inner value.
92 ///
93 /// Note that this is UB if the inner value is currently dangling.
94 pub const fn as_mut(&mut self) -> &mut P {
95 &mut self.0
96 }
97
98 /// Extracts the value from the `MaybeDangling` container.
99 ///
100 /// Note that this is UB if the inner value is currently dangling.
101 pub const fn into_inner(self) -> P
102 where
103 P: Sized,
104 {
105 // FIXME: replace this with `self.0` when const checker can figure out that `self` isn't actually dropped
106 // SAFETY: this is equivalent to `self.0`
107 let x = unsafe { ptr::read(&self.0) };
108 mem::forget(self);
109 x
110 }
111}