Skip to content

Commit 6695a56

Browse files
committed
Document subtleties of ManuallyDrop
1 parent 2699de6 commit 6695a56

File tree

1 file changed

+115
-11
lines changed

1 file changed

+115
-11
lines changed

core/src/mem/manually_drop.rs

Lines changed: 115 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
use crate::ops::{Deref, DerefMut, DerefPure};
22
use crate::ptr;
33

4-
/// A wrapper to inhibit the compiler from automatically calling `T`’s destructor.
5-
/// This wrapper is 0-cost.
4+
/// A wrapper to inhibit the compiler from automatically calling `T`’s
5+
/// destructor. This wrapper is 0-cost.
66
///
77
/// `ManuallyDrop<T>` is guaranteed to have the same layout and bit validity as
8-
/// `T`, and is subject to the same layout optimizations as `T`. As a consequence,
9-
/// it has *no effect* on the assumptions that the compiler makes about its
10-
/// contents. For example, initializing a `ManuallyDrop<&mut T>` with [`mem::zeroed`]
11-
/// is undefined behavior. If you need to handle uninitialized data, use
12-
/// [`MaybeUninit<T>`] instead.
8+
/// `T`, and is subject to the same layout optimizations as `T`. As a
9+
/// consequence, it has *no effect* on the assumptions that the compiler makes
10+
/// about its contents. For example, initializing a `ManuallyDrop<&mut T>` with
11+
/// [`mem::zeroed`] is undefined behavior. If you need to handle uninitialized
12+
/// data, use [`MaybeUninit<T>`] instead.
1313
///
14-
/// Note that accessing the value inside a `ManuallyDrop<T>` is safe.
15-
/// This means that a `ManuallyDrop<T>` whose content has been dropped must not
16-
/// be exposed through a public safe API.
17-
/// Correspondingly, `ManuallyDrop::drop` is unsafe.
14+
/// Note that accessing the value inside a `ManuallyDrop<T>` is safe. This means
15+
/// that a `ManuallyDrop<T>` whose content has been dropped must not be exposed
16+
/// through a public safe API. Correspondingly, `ManuallyDrop::drop` is unsafe.
1817
///
1918
/// # `ManuallyDrop` and drop order.
2019
///
@@ -40,9 +39,114 @@ use crate::ptr;
4039
/// }
4140
/// ```
4241
///
42+
/// # Interaction with `Box`.
43+
///
44+
/// Currently, once the `Box<T>` inside a `ManuallyDrop<Box<T>>` is dropped,
45+
/// moving the `ManuallyDrop<Box<T>>` is [considered to be undefined
46+
/// behavior](https://github.com/rust-lang/unsafe-code-guidelines/issues/245).
47+
/// That is, the following code causes undefined behavior:
48+
///
49+
/// ```no_run
50+
/// use std::mem::ManuallyDrop;
51+
///
52+
/// let mut x = ManuallyDrop::new(Box::new(42));
53+
/// unsafe {
54+
/// ManuallyDrop::drop(&mut x);
55+
/// }
56+
/// let y = x; // Undefined behavior!
57+
/// ```
58+
///
59+
/// This may change in the future. In the meantime, consider using
60+
/// [`MaybeUninit`] instead.
61+
///
62+
/// # Safety hazards when storing `ManuallyDrop` in a struct / enum.
63+
///
64+
/// Special care is needed when all of the conditions below are met:
65+
/// * A field of a struct or enum is a `ManuallyDrop` or contains a
66+
/// `ManuallyDrop`, without the `ManuallyDrop` being inside a `union`.
67+
/// * The struct or enum is part of public API, or is stored in a struct or enum
68+
/// that is part of public API.
69+
/// * There is code outside of a `Drop` implementation that calls
70+
/// [`ManuallyDrop::drop`] or [`ManuallyDrop::take`] on the `ManuallyDrop`
71+
/// field.
72+
///
73+
/// In particular, the following hazards can occur:
74+
///
75+
/// #### Storing generic types
76+
///
77+
/// If the `ManuallyDrop` contains a client-supplied generic type, the client
78+
/// might provide a `Box`, causing undefined behavior when the struct / enum is
79+
/// later moved, as mentioned above. For example, the following code causes
80+
/// undefined behavior:
81+
///
82+
/// ```no_run
83+
/// use std::mem::ManuallyDrop;
84+
///
85+
/// pub struct BadOption<T> {
86+
/// // Invariant: Has been dropped iff `is_some` is false.
87+
/// value: ManuallyDrop<T>,
88+
/// is_some: bool,
89+
/// }
90+
/// impl<T> BadOption<T> {
91+
/// pub fn new(value: T) -> Self {
92+
/// Self { value: ManuallyDrop::new(value), is_some: true }
93+
/// }
94+
/// pub fn change_to_none(&mut self) {
95+
/// if self.is_some {
96+
/// self.is_some = false;
97+
/// unsafe {
98+
/// // SAFETY: `value` hasn't been dropped yet, as per the invariant
99+
/// // (This is actually unsound!)
100+
/// ManuallyDrop::drop(&mut self.value);
101+
/// }
102+
/// }
103+
/// }
104+
/// }
105+
///
106+
/// // In another crate:
107+
///
108+
/// let mut option = BadOption::new(Box::new(42));
109+
/// option.change_to_none();
110+
/// let option2 = option; // Undefined behavior!
111+
/// ```
112+
///
113+
/// #### Deriving traits
114+
///
115+
/// Deriving `Debug`, `Clone`, `PartialEq`, `PartialOrd`, `Ord`, or `Hash` on
116+
/// the struct / enum could be unsound, since the derived implementations of
117+
/// these traits would access the `ManuallyDrop` field. For example, the
118+
/// following code causes undefined behavior:
119+
///
120+
/// ```no_run
121+
/// use std::mem::ManuallyDrop;
122+
///
123+
/// #[derive(Debug)] // This is unsound!
124+
/// pub struct Foo {
125+
/// value: ManuallyDrop<String>,
126+
/// }
127+
/// impl Foo {
128+
/// pub fn new() -> Self {
129+
/// let mut temp = Self {
130+
/// value: ManuallyDrop::new(String::from("Unsafe rust is hard"))
131+
/// };
132+
/// unsafe {
133+
/// // SAFETY: `value` hasn't been dropped yet.
134+
/// ManuallyDrop::drop(&mut temp.value);
135+
/// }
136+
/// temp
137+
/// }
138+
/// }
139+
///
140+
/// // In another crate:
141+
///
142+
/// let foo = Foo::new();
143+
/// println!("{:?}", foo); // Undefined behavior!
144+
/// ```
145+
///
43146
/// [drop order]: https://doc.rust-lang.org/reference/destructors.html
44147
/// [`mem::zeroed`]: crate::mem::zeroed
45148
/// [`MaybeUninit<T>`]: crate::mem::MaybeUninit
149+
/// [`MaybeUninit`]: crate::mem::MaybeUninit
46150
#[stable(feature = "manually_drop", since = "1.20.0")]
47151
#[lang = "manually_drop"]
48152
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy