Skip to content

Commit 8520b8e

Browse files
committed
document that ManuallyDrop's Box interaction has been fixed
1 parent f92acd0 commit 8520b8e

1 file changed

Lines changed: 44 additions & 51 deletions

File tree

library/core/src/mem/manually_drop.rs

Lines changed: 44 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -57,56 +57,14 @@ use crate::ptr;
5757
/// * There is code that drops the contents of the `ManuallyDrop` field, and
5858
/// this code is outside the struct or enum's `Drop` implementation.
5959
///
60-
/// In particular, the following hazards may occur:
61-
///
62-
/// #### Storing generic types
63-
///
64-
/// If the `ManuallyDrop` contains a client-supplied generic type, the client
65-
/// might provide a `Box` as that type. This would cause undefined behavior when
66-
/// the struct or enum is later moved, as mentioned in the previous section. For
60+
/// In particular, deriving `Debug`, `Clone`, `PartialEq`, `PartialOrd`, `Ord`,
61+
/// or `Hash` on the struct or enum could be unsound, since the derived
62+
/// implementations of these traits would access the `ManuallyDrop` field. For
6763
/// example, the following code causes undefined behavior:
6864
///
6965
/// ```no_run
7066
/// use std::mem::ManuallyDrop;
7167
///
72-
/// pub struct BadOption<T> {
73-
/// // Invariant: Has been dropped if `is_some` is false.
74-
/// value: ManuallyDrop<T>,
75-
/// is_some: bool,
76-
/// }
77-
/// impl<T> BadOption<T> {
78-
/// pub fn new(value: T) -> Self {
79-
/// Self { value: ManuallyDrop::new(value), is_some: true }
80-
/// }
81-
/// pub fn change_to_none(&mut self) {
82-
/// if self.is_some {
83-
/// self.is_some = false;
84-
/// unsafe {
85-
/// // SAFETY: `value` hasn't been dropped yet, as per the invariant
86-
/// // (This is actually unsound!)
87-
/// ManuallyDrop::drop(&mut self.value);
88-
/// }
89-
/// }
90-
/// }
91-
/// }
92-
///
93-
/// // In another crate:
94-
///
95-
/// let mut option = BadOption::new(Box::new(42));
96-
/// option.change_to_none();
97-
/// let option2 = option; // Undefined behavior!
98-
/// ```
99-
///
100-
/// #### Deriving traits
101-
///
102-
/// Deriving `Debug`, `Clone`, `PartialEq`, `PartialOrd`, `Ord`, or `Hash` on
103-
/// the struct or enum could be unsound, since the derived implementations of
104-
/// these traits would access the `ManuallyDrop` field. For example, the
105-
/// following code causes undefined behavior:
106-
///
107-
/// ```no_run
108-
/// use std::mem::ManuallyDrop;
109-
///
11068
/// // This derive is unsound in combination with the `ManuallyDrop::drop` call.
11169
/// #[derive(Debug)]
11270
/// pub struct Foo {
@@ -131,13 +89,13 @@ use crate::ptr;
13189
/// println!("{:?}", foo); // Undefined behavior!
13290
/// ```
13391
///
134-
/// # Interaction with `Box`
92+
/// # Pre-`1.96` Interaction with `Box`
13593
///
136-
/// Currently, if you have a `ManuallyDrop<T>`, where the type `T` is a `Box` or
137-
/// contains a `Box` inside, then dropping the `T` followed by moving the
138-
/// `ManuallyDrop<T>` is [considered to be undefined
94+
/// Before Rust `1.96.0`, if you had a `ManuallyDrop<T>`, where the type `T`
95+
/// was a `Box` or contained a `Box` inside, then dropping the `T` followed by
96+
/// moving the `ManuallyDrop<T>` was [considered to be undefined
13997
/// behavior](https://github.com/rust-lang/unsafe-code-guidelines/issues/245).
140-
/// That is, the following code causes undefined behavior:
98+
/// That is, the following code caused undefined behavior:
14199
///
142100
/// ```no_run
143101
/// use std::mem::ManuallyDrop;
@@ -146,7 +104,42 @@ use crate::ptr;
146104
/// unsafe {
147105
/// ManuallyDrop::drop(&mut x);
148106
/// }
149-
/// let y = x; // Undefined behavior!
107+
/// let y = x; // Undefined behavior! (pre 1.96.0)
108+
/// ```
109+
///
110+
/// Note that this could also have happen with a generic type where the user of
111+
/// the library providing it could substitute the generic for a `Box<_>` and
112+
/// then move the library type:
113+
///
114+
/// ```no_run
115+
/// use std::mem::ManuallyDrop;
116+
///
117+
/// pub struct BadOption<T> {
118+
/// // Invariant: Has been dropped if `is_some` is false.
119+
/// value: ManuallyDrop<T>,
120+
/// is_some: bool,
121+
/// }
122+
/// impl<T> BadOption<T> {
123+
/// pub fn new(value: T) -> Self {
124+
/// Self { value: ManuallyDrop::new(value), is_some: true }
125+
/// }
126+
/// pub fn change_to_none(&mut self) {
127+
/// if self.is_some {
128+
/// self.is_some = false;
129+
/// unsafe {
130+
/// // SAFETY: `value` hasn't been dropped yet, as per the invariant
131+
/// // (This is actually unsound pre rust 1.96.0!)
132+
/// ManuallyDrop::drop(&mut self.value);
133+
/// }
134+
/// }
135+
/// }
136+
/// }
137+
///
138+
/// // In another crate:
139+
///
140+
/// let mut option = BadOption::new(Box::new(42));
141+
/// option.change_to_none();
142+
/// let option2 = option; // Undefined behavior! (pre 1.96)
150143
/// ```
151144
///
152145
/// [drop order]: https://doc.rust-lang.org/reference/destructors.html

0 commit comments

Comments
 (0)