Skip to main content

maybe_dangling/
maybe_dangling.rs

1// The one usage of `StdMD` in this file does not need `MaybeDangling` semantics in it.
2use ::core::mem::ManuallyDrop as StdMD;
3// This used to be `crate::ManuallyDrop` (i.e., with `MaybeDangling` semantics in it),
4// but we can directly use the one from stdlib
5use ::core::mem::ManuallyDrop;
6
7/// Like [`crate::ManuallyDrop`] but for having `drop` glue.
8/// This wrapper is 0-cost.
9///
10/// In other words, a <code>[MaybeDangling]\<T\></code> is just like `T`, but
11/// for having been stripped of aliasing/`dereferenceable`-ity properties.
12///
13/// Its usage should be quite rare and advanced: if you are intending to keep
14/// hold of a potentially dangling / exhausted value, chances are you won't
15/// want implicit/automatic drop glue of it without having previously checked
16/// for lack of exhaustion ⚠️.
17///
18/// That is, it is strongly advisable to be using
19/// <code>[crate::ManuallyDrop]\<T\></code> instead!
20///
21/// ### Opting into unstable `#[may_dangle]` and the `dropck_eyepatch`
22///
23/// Ironically, for this drop glue to be as smooth as it should be, the unstable
24/// `#[may_dangle]` feature is needed.
25///
26/// But by virtue of being unstable, it cannot be offered by this crate on
27/// stable Rust.
28///
29/// For the adventurous `nightly` users, you can enable the
30/// `nightly-dropck_eyepatch` Cargo feature to opt into the usage of the
31/// [eponymous `rustc` feature][RFC-1327] so as to get the `Drop` implementation
32/// amended accordingly.
33///
34/// Explanation:
35///
36/// <details class="custom"><summary><span class="summary-box"><span>Click to show</span></span></summary>
37///
38/// #### What does it mean to have a "dangling `T`"?
39///
40/// Note that the terminology of a "dangling `T`" can be a bit hard to
41/// visualize. The idea is to consider some `'dangling` lifetime (_e.g._,
42/// some `&'dangling` borrow), to then imagine different type definitions
43/// involving it.
44///
45/// For instance:
46///
47///  1. `T = &'dangling str`
48///  2. `T = PrintOnDrop<&'dangling str>`,
49///  3. `T = Box<&'dangling str>`,
50///  4. `T = (u8, Box<PrintOnDrop<&'dangling str>>)`,
51///  5. `T = &mut PrintOnDrop<&'dangling str>`,
52///
53/// The key idea is that there are three categories of types here:
54///
55///   1. The types with no drop glue at all, _i.e._, types for which
56///      `mem::needs_drop::<T>()` returns `false`: types 1. and 5.
57///
58///      Such types _should be allowed_ to go out of scope at a point
59///      where their lifetime may be `'dangling`.
60///
61///   2. The types with drop glue known not to involve a dereference of
62///      the `&'dangling` reference: type 3.
63///
64///      Such types _can be allowed_ to go out of scope (and thus, run
65///      their drop glue) at a point where their lifetime may be
66///      `'dangling`.
67///
68///   3. The types with drop glue (potentially) involving a dereference
69///      of the `&'dangling` reference: types 2. and 4.
70///
71///      Such types _should never be allowed_ to go out of scope at a
72///      point where their lifetime may be `'dangling`.
73///
74/// Notice how a useful distinction thus revolves around the presence
75/// of drop glue or lack thereof, to determine whether we are in the
76/// first category, or the other two. On the other hand, whether a type
77/// _directly_ implements `Drop`, such as `Box` or `PrintOnDrop`, or
78/// does not (wrapper types containing it, such as `String` w.r.t. the
79/// `Drop` implementation of the underlying `Vec<u8>`, or `(u8, Box<...>)`
80/// in the fourth example type above), is not enough information to
81/// distinguish between the two, as
82///
83///   - types 2. and 3. both implement `Drop`, and yet belong to different
84///     categories,
85///
86///   - type 4. does not implement `Drop`, and yet belongs to the same
87///     category as type 2.
88///
89/// See the [`drop_bounds` lint] for more info.
90///
91/// The distinction between the second and third category is whether a generic
92/// type, when dropped,
93///
94/// 1. merely drops its inner `T` (like `Box<T>` does) and
95///
96/// 2. makes it known to the [drop checker] that it does so.
97///
98/// If a type violates either restriction, either by unconditionally using any
99/// other API of `T`, like `PrintOnDrop<T: Debug>` does, or by not making
100/// it known to the drop checker that it merely drops its inner `T`, it will
101/// belong to category 3, which can't be allowed to compile.
102///
103/// Making it known to the drop checker that `T` is merely dropped requires
104/// the unstable [`#[may_dangle]`][RFC-1327] attribute.
105/// The drop checker does not know the implementation details of any
106/// `Drop` implementation.
107/// It can't statically analyse how `T` is used in the destructor.
108/// Instead, drop check requires every generic argument to strictly
109/// outlive the wrapper type to guarantee soundness.
110/// This can be overly restrictive when merely dropping `T`, making it
111/// impossible to have `Drop` implementations where `T` might be dangling,
112/// even if dropping a dangling `T` would be sound in the given context.
113/// Hence the `#[may_dangle]` attribute is required to manually and _unsafely_
114/// tell drop check that `T` is merely dropped in the generic type's
115/// destructor, relaxing the drop checker in situations where its soundness
116/// requirements are overly restrictive.
117/// With the `nightly-dropck_eyepatch` feature enabled, <code>[MaybeDangling]\<T\></code>
118/// uses `#[may_dangle]` under the hood to let drop check know that it won't
119/// access the potentially dangling `T` (_e.g._, the `str` behind
120/// `T = &'dangling str`) in its destructor, [*unless*][dropck-generics] `T`'s
121/// `'dangling` lifetime is involved in transitive drop glue, _i.e._:
122///   - whenever `T` implements `Drop` (without `#[may_dangle]`);
123///   - or whenever `T` transitively owns some field with drop glue involving
124///     `'dangling`.
125///
126/// With that context in mind, let's look at examples for the three categories:
127///
128/// #### Category 1: `T` has no drop glue (_e.g._, `T = &'dangling str`)
129///
130/// Since `T` does not have drop glue (`mem::needs_drop::<T>()` returns `false`),
131/// the drop checker will allow this to compile, even though the reference
132/// dangles when `v` gets dropped:
133///
134/// ```
135/// # #[cfg(feature = "nightly-dropck_eyepatch")]
136/// # {
137/// use ::maybe_dangling::MaybeDangling;
138///
139/// fn main() {
140///     let s: String = "I will dangle".into();
141///     let v = MaybeDangling::new(&s);
142///     drop(s); // <- makes `&s` dangle
143/// } // <- `v` dropped here, despite containing a `&'dangling s` reference!
144/// # }
145/// ```
146///
147/// #### Category 2: `T` has drop glue known not to involve `'dangling` (_e.g._, `T = Box<&'dangling str>`)
148///
149/// Now that `T` is has drop glue, it must be executed when `v` is dropped.
150/// `Box<&'dangling str>`'s `Drop` implementation is known not to involve
151/// `'dangling`, so it is safe for `&'dangling str` to dangle when the `Box`
152/// is dropped:
153///
154/// ```
155/// # #![cfg_attr(feature = "nightly-dropck_eyepatch", feature(dropck_eyepatch))]
156/// # #[cfg(feature = "nightly-dropck_eyepatch")]
157/// # {
158/// use ::maybe_dangling::MaybeDangling;
159///
160/// fn main() {
161///     let s: String = "I will dangle".into();
162///     let v = MaybeDangling::new(Box::new(&s));
163///     drop(s); // <- makes `&s` dangle
164/// } // <- `v`, and thus `Box(&s)` dropped here
165/// # }
166/// ```
167///
168/// #### Category 3: `T` has drop glue (potentially) involving `'dangling` (_e.g._, `T = PrintOnDrop<&'dangling str>`)
169///
170/// Like the second category, `T` now has drop glue.
171/// But unlike category 2., `T` now has drop glue either involving `'dangling`
172/// or not informing the drop checker that `'dangling` is unused.
173/// Let's look at an example where `'dangling` is involved in drop glue:
174///
175/// ```compile_fail
176/// use ::maybe_dangling::MaybeDangling;
177///
178/// use ::std::fmt::Debug;
179///
180/// struct PrintOnDrop<T: Debug>(T);
181///
182/// impl<T: Debug> Drop for PrintOnDrop<T> {
183///     fn drop(&mut self) {
184///          println!("Using the potentially dangling `T` in our destructor: {:?}", self.0);
185///     }
186/// }
187///
188/// fn main() {
189///     let s: String = "I will dangle".into();
190///     let v = MaybeDangling::new(PrintOnDrop(&s));
191///     drop(s); // <- makes `&s` dangle
192/// } // <- `v`, and thus `PrintOnDrop(&s)` dropped here, causing a use-after-free ! ⚠️
193/// ```
194///
195/// The example above should never be allowed to compile as `PrintOnDrop`
196/// will dereference `&'dangling str`, which points to a `str` that already
197/// got dropped and invalidated, causing undefined behavior.
198///
199/// An example for a type where `'dangling` is not involved in any drop glue
200/// but does not relax the drop checker with `#[may_dangle]` would be:
201///
202/// ```compile_fail
203/// use ::maybe_dangling::MaybeDangling;
204///
205/// struct MerelyDrop<T>(T);
206///
207/// impl<T> Drop for MerelyDrop<T> {
208///     fn drop(&mut self) {
209///          println!("Not using the potentially dangling `T` in our destructor");
210///     }
211/// }
212///
213/// fn main() {
214///     let s: String = "I will dangle".into();
215///     let v = MaybeDangling::new(MerelyDrop(&s));
216///     drop(s); // <- makes `&s` dangle
217/// } // <- `v`, and thus `MerelyDrop(&s)` dropped here
218/// ```
219///
220/// To amend the example above and move from category 3. to category 2. and
221/// make it compile, `#[may_dangle]` can be applied to `T` in `MerelyDrop`'s
222/// `Drop` implementation:
223///
224/// ```
225/// # #![cfg_attr(feature = "nightly-dropck_eyepatch", feature(dropck_eyepatch))]
226/// # #[cfg(feature = "nightly-dropck_eyepatch")]
227/// # {
228/// #![feature(dropck_eyepatch)]
229///
230/// use ::maybe_dangling::MaybeDangling;
231///
232/// struct MerelyDrop<T>(T);
233///
234/// unsafe impl<#[may_dangle] T> Drop for MerelyDrop<T> {
235///     fn drop(&mut self) {
236///          println!("Not using the potentially dangling `T` in our destructor");
237///     }
238/// }
239///
240/// fn main() {
241///     let s: String = "I will dangle".into();
242///     let v = MaybeDangling::new(MerelyDrop(&s));
243///     drop(s); // <- makes `&s` dangle
244/// } // <- `v`, and thus `MerelyDrop(&s)` dropped here
245/// # }
246/// ```
247///
248/// Note that the `Drop` implementation is _unsafe_ now, as we are still free
249/// to use the dangling `T` in the destructor.
250/// We only pinky-swear to the drop checker that we won't.
251///
252/// </details>
253///
254/// #### Summary: when is a `MaybeDangling<...'dangling...>` allowed to go out of scope
255///
256/// This table summarises which of the categories shown above can be compiled, with
257/// or without the `nightly-dropck_eyepatch` feature enabled:
258///
259/// | `MaybeDangling<T>`<br/><br/>`where T` | With `nightly-dropck_eyepatch` | Without `nightly-dropck_eyepatch` |
260/// | --- | --- | --- |
261/// | has no drop glue<br/>_e.g._<br/>`T = &'dangling str` | ✅ | ❌ |
262/// | has drop glue known not to involve `'dangling`<br/>_e.g._<br/>`T = Box<&'dangling str>` | ✅ | ❌ |
263/// | has drop glue (potentially) involving `'dangling`<br/>_e.g._<br/>`T = PrintOnDrop<&'dangling str>` | ❌ | ❌ |
264///
265/// [RFC-1327]: https://rust-lang.github.io/rfcs/1327-dropck-param-eyepatch.html
266/// [`drop_bounds` lint]: https://doc.rust-lang.org/1.71.0/nightly-rustc/rustc_lint/traits/static.DROP_BOUNDS.html#explanation
267/// [drop checker]: https://doc.rust-lang.org/1.71.0/nomicon/dropck.html
268/// [dropck-generics]: https://doc.rust-lang.org/1.71.0/nomicon/phantom-data.html#generic-parameters-and-drop-checking
269#[repr(transparent)]
270pub struct MaybeDangling<T> {
271    value: ManuallyDrop<T>,
272    #[cfg(feature = "nightly-dropck_eyepatch")]
273    #[allow(nonstandard_style)]
274    // disables `#[may_dangle]` for `T` invovled in transitive drop glue
275    _owns_T: ::core::marker::PhantomData<T>,
276}
277
278impl<T> MaybeDangling<T> {
279    pub const fn new(value: T) -> MaybeDangling<T> {
280        Self {
281            value: ManuallyDrop::new(value),
282            #[cfg(feature = "nightly-dropck_eyepatch")]
283            _owns_T: ::core::marker::PhantomData,
284        }
285    }
286
287    /// Extracts the value from the `MaybeDangling` container.
288    ///
289    /// See [`::core::mem::ManuallyDrop::into_inner()`] for more info.
290    #[inline]
291    pub fn into_inner(slot: MaybeDangling<T>) -> T {
292        #![allow(unsafe_code)]
293        // Safety: this is the defuse inherent drop glue pattern.
294        unsafe { ManuallyDrop::take(&mut StdMD::new(slot).value) }
295    }
296
297    /// Akin to [`ManuallyDrop::drop()`]: it drops the inner value **in-place**. Raw & `unsafe`
298    /// version of [`drop_in_place!`].
299    ///
300    /// [`drop_in_place!`]: `crate::drop_in_place!`
301    ///
302    /// `Pin` code can rely on this guarantee: [an example](https://docs.rs/droppable_pin).
303    ///
304    /// # Safety
305    ///
306    /// This API is `unsafe` and wildly dangerous. It is very strongly advisable to use
307    /// [`drop_in_place!`] instead.
308    ///
309    /// Indeed, since [`MaybeDangling`] does have embedded drop glue, the moment this function
310    /// returns the only thing that ought to be done is immediately [`::core::mem::forget()`]ting it
311    /// (or wrapping it in a [`ManuallyDrop`]), lest it be dropped implicitly (_e.g._, because of
312    /// some panic), resulting un double-drop unsoundness 😱.
313    ///
314    /// As a matter of fact, this very function needs to feature an abort-on-panic guard to
315    /// handle this problem internally.
316    #[allow(unsafe_code)]
317    pub unsafe fn drop_in_place(this: &mut MaybeDangling<T>) {
318        struct PanicOnDrop();
319        impl Drop for PanicOnDrop {
320            fn drop(&mut self) {
321                panic!("aborting for soundness");
322            }
323        }
324        let _guard = PanicOnDrop();
325        // Unwind-safety: if this unwinds, the `_guard` is dropped, which panics, resulting in a
326        // nested panic which causes the process to abort.
327        unsafe {
328            ManuallyDrop::drop(&mut this.value);
329        }
330        ::core::mem::forget(_guard);
331    }
332}
333
334// The main difference with `ManuallyDrop`: automatic drop glue!
335cfg_select! {
336    feature = "nightly-dropck_eyepatch" => {
337        #[allow(unsafe_code)]
338        unsafe impl<#[may_dangle] T> Drop for MaybeDangling<T> {
339            fn drop(&mut self) {
340                unsafe {
341                    ManuallyDrop::drop(&mut self.value)
342                }
343            }
344        }
345    },
346
347    _ => {
348        impl<T> Drop for MaybeDangling<T> {
349            fn drop(&mut self) {
350                #![allow(unsafe_code)]
351                unsafe {
352                    ManuallyDrop::drop(&mut self.value)
353                }
354            }
355        }
356    },
357}
358
359impl<T> ::core::ops::DerefMut for MaybeDangling<T> {
360    #[inline]
361    fn deref_mut(&mut self) -> &mut T {
362        #[expect(non_local_definitions)]
363        impl<T> ::core::ops::Deref for MaybeDangling<T> {
364            type Target = T;
365
366            #[inline]
367            fn deref(self: &Self) -> &T {
368                let Self { value, .. } = self;
369                value
370            }
371        }
372
373        let Self { value, .. } = self;
374        value
375    }
376}
377
378impl<T: Default> Default for MaybeDangling<T> {
379    #[inline]
380    fn default() -> Self {
381        Self::new(T::default())
382    }
383}
384
385impl<T: Clone> Clone for MaybeDangling<T> {
386    fn clone(self: &Self) -> Self {
387        Self::new(T::clone(self))
388    }
389
390    fn clone_from(self: &mut Self, source: &Self) {
391        T::clone_from(self, source)
392    }
393}
394
395/// Safe API around [`MaybeDangling::drop_in_place()`], which performs the mandatory
396/// [`::core::mem::forget()`] on the given var.
397///
398/// Equivalent to doing <code>[drop]::\<[MaybeDangling]\>\($var\)</code>, but for not moving the
399/// given `$var` before doing so (important with, for instance, `Pin` stuff).
400///
401///   - Using [`MaybeDangling::drop_in_place()`] directly is so wildly dangerous that it is
402///     discouraged.
403///
404///   - Using [`ManuallyDrop`] alongside [`ManuallyDrop::drop()`] is significantly less dangerous
405///     w.r.t. double-dropping, but alas just as dangerous w.r.t. leaking when dealing with the
406///     `Pin` contract for which a lack of drop in certain cases is just as unsound.
407///
408/// Remark: this macro requires the given `$var` binding to have been declared `mut`able.
409///
410/// # Example
411///
412/// Imagine, as a library author, wanting to offer the following kind of API:
413///
414/// ```rust
415/// # fn stuff() {}
416/// pub use ::core;
417/// # pub extern crate maybe_dangling; /*
418/// pub use ::maybe_dangling;
419/// # */
420///
421/// macro_rules! my_droppable_pin {(
422///     let mut $var:ident = pin!($value:expr);
423/// ) => (
424///     let mut pinned_value = $crate::maybe_dangling::MaybeDangling::new($value);
425///     macro_rules! drop_it {() => (
426///         $crate::maybe_dangling::drop_in_place!(pinned_value);
427///     )}
428///     #[allow(unused_mut)]
429///     let mut $var = unsafe {
430///         $crate::core::pin::Pin::new_unchecked(&mut *pinned_value)
431///     };
432/// )}
433///
434/// fn main() {
435///     use ::core::{marker::PhantomPinned, pin::*};
436///
437///     my_droppable_pin! {
438///         let mut p = pin!(PhantomPinned);
439///     }
440///     let _: Pin<&mut PhantomPinned> = p.as_mut(); // properly pinned!
441///     for i in 0.. {
442///         if i == 5 {
443///             drop_it!(); // drops the `PhantomPinned` in-place, abiding by the drop guarantee.
444///             // stuff runs after `PhantomPinned` has been dropped, rather than before.
445///             stuff();
446///             break;
447///         }
448///     }
449/// }
450/// ```
451#[macro_export]
452macro_rules! drop_in_place {
453    ( $var:ident $(,)? ) => {
454        // guard against `$var` not being a place (e.g., some `const` or `static mut` or whatnot).
455        _ = &raw const $var;
456        unsafe {
457            $crate::MaybeDangling::drop_in_place(&mut $var);
458        }
459        $crate::ඞ::core::mem::forget($var);
460    };
461}