From f92acd0edc520e55fe7bdc7b74598aedb04f0878 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Fri, 24 Apr 2026 19:55:52 +0200 Subject: [PATCH 1/2] move a section in a doc comment it is not as important anymore (see next comment) --- library/core/src/mem/manually_drop.rs | 36 +++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/library/core/src/mem/manually_drop.rs b/library/core/src/mem/manually_drop.rs index ca008a82ee6f0..5be977677e0f9 100644 --- a/library/core/src/mem/manually_drop.rs +++ b/library/core/src/mem/manually_drop.rs @@ -43,24 +43,6 @@ use crate::ptr; /// } /// ``` /// -/// # Interaction with `Box` -/// -/// Currently, if you have a `ManuallyDrop`, where the type `T` is a `Box` or -/// contains a `Box` inside, then dropping the `T` followed by moving the -/// `ManuallyDrop` is [considered to be undefined -/// behavior](https://github.com/rust-lang/unsafe-code-guidelines/issues/245). -/// That is, the following code causes undefined behavior: -/// -/// ```no_run -/// use std::mem::ManuallyDrop; -/// -/// let mut x = ManuallyDrop::new(Box::new(42)); -/// unsafe { -/// ManuallyDrop::drop(&mut x); -/// } -/// let y = x; // Undefined behavior! -/// ``` -/// /// This is [likely to change in the /// future](https://rust-lang.github.io/rfcs/3336-maybe-dangling.html). In the /// meantime, consider using [`MaybeUninit`] instead. @@ -149,6 +131,24 @@ use crate::ptr; /// println!("{:?}", foo); // Undefined behavior! /// ``` /// +/// # Interaction with `Box` +/// +/// Currently, if you have a `ManuallyDrop`, where the type `T` is a `Box` or +/// contains a `Box` inside, then dropping the `T` followed by moving the +/// `ManuallyDrop` is [considered to be undefined +/// behavior](https://github.com/rust-lang/unsafe-code-guidelines/issues/245). +/// That is, the following code causes undefined behavior: +/// +/// ```no_run +/// use std::mem::ManuallyDrop; +/// +/// let mut x = ManuallyDrop::new(Box::new(42)); +/// unsafe { +/// ManuallyDrop::drop(&mut x); +/// } +/// let y = x; // Undefined behavior! +/// ``` +/// /// [drop order]: https://doc.rust-lang.org/reference/destructors.html /// [`mem::zeroed`]: crate::mem::zeroed /// [`MaybeUninit`]: crate::mem::MaybeUninit From 8520b8e11a76da49bb920e2ab8729185c543a376 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Fri, 24 Apr 2026 19:57:56 +0200 Subject: [PATCH 2/2] document that `ManuallyDrop`'s `Box` interaction has been fixed --- library/core/src/mem/manually_drop.rs | 95 +++++++++++++-------------- 1 file changed, 44 insertions(+), 51 deletions(-) diff --git a/library/core/src/mem/manually_drop.rs b/library/core/src/mem/manually_drop.rs index 5be977677e0f9..4d9828b91061a 100644 --- a/library/core/src/mem/manually_drop.rs +++ b/library/core/src/mem/manually_drop.rs @@ -57,56 +57,14 @@ use crate::ptr; /// * There is code that drops the contents of the `ManuallyDrop` field, and /// this code is outside the struct or enum's `Drop` implementation. /// -/// In particular, the following hazards may occur: -/// -/// #### Storing generic types -/// -/// If the `ManuallyDrop` contains a client-supplied generic type, the client -/// might provide a `Box` as that type. This would cause undefined behavior when -/// the struct or enum is later moved, as mentioned in the previous section. For +/// In particular, deriving `Debug`, `Clone`, `PartialEq`, `PartialOrd`, `Ord`, +/// or `Hash` on the struct or enum could be unsound, since the derived +/// implementations of these traits would access the `ManuallyDrop` field. For /// example, the following code causes undefined behavior: /// /// ```no_run /// use std::mem::ManuallyDrop; /// -/// pub struct BadOption { -/// // Invariant: Has been dropped if `is_some` is false. -/// value: ManuallyDrop, -/// is_some: bool, -/// } -/// impl BadOption { -/// pub fn new(value: T) -> Self { -/// Self { value: ManuallyDrop::new(value), is_some: true } -/// } -/// pub fn change_to_none(&mut self) { -/// if self.is_some { -/// self.is_some = false; -/// unsafe { -/// // SAFETY: `value` hasn't been dropped yet, as per the invariant -/// // (This is actually unsound!) -/// ManuallyDrop::drop(&mut self.value); -/// } -/// } -/// } -/// } -/// -/// // In another crate: -/// -/// let mut option = BadOption::new(Box::new(42)); -/// option.change_to_none(); -/// let option2 = option; // Undefined behavior! -/// ``` -/// -/// #### Deriving traits -/// -/// Deriving `Debug`, `Clone`, `PartialEq`, `PartialOrd`, `Ord`, or `Hash` on -/// the struct or enum could be unsound, since the derived implementations of -/// these traits would access the `ManuallyDrop` field. For example, the -/// following code causes undefined behavior: -/// -/// ```no_run -/// use std::mem::ManuallyDrop; -/// /// // This derive is unsound in combination with the `ManuallyDrop::drop` call. /// #[derive(Debug)] /// pub struct Foo { @@ -131,13 +89,13 @@ use crate::ptr; /// println!("{:?}", foo); // Undefined behavior! /// ``` /// -/// # Interaction with `Box` +/// # Pre-`1.96` Interaction with `Box` /// -/// Currently, if you have a `ManuallyDrop`, where the type `T` is a `Box` or -/// contains a `Box` inside, then dropping the `T` followed by moving the -/// `ManuallyDrop` is [considered to be undefined +/// Before Rust `1.96.0`, if you had a `ManuallyDrop`, where the type `T` +/// was a `Box` or contained a `Box` inside, then dropping the `T` followed by +/// moving the `ManuallyDrop` was [considered to be undefined /// behavior](https://github.com/rust-lang/unsafe-code-guidelines/issues/245). -/// That is, the following code causes undefined behavior: +/// That is, the following code caused undefined behavior: /// /// ```no_run /// use std::mem::ManuallyDrop; @@ -146,7 +104,42 @@ use crate::ptr; /// unsafe { /// ManuallyDrop::drop(&mut x); /// } -/// let y = x; // Undefined behavior! +/// let y = x; // Undefined behavior! (pre 1.96.0) +/// ``` +/// +/// Note that this could also have happen with a generic type where the user of +/// the library providing it could substitute the generic for a `Box<_>` and +/// then move the library type: +/// +/// ```no_run +/// use std::mem::ManuallyDrop; +/// +/// pub struct BadOption { +/// // Invariant: Has been dropped if `is_some` is false. +/// value: ManuallyDrop, +/// is_some: bool, +/// } +/// impl BadOption { +/// pub fn new(value: T) -> Self { +/// Self { value: ManuallyDrop::new(value), is_some: true } +/// } +/// pub fn change_to_none(&mut self) { +/// if self.is_some { +/// self.is_some = false; +/// unsafe { +/// // SAFETY: `value` hasn't been dropped yet, as per the invariant +/// // (This is actually unsound pre rust 1.96.0!) +/// ManuallyDrop::drop(&mut self.value); +/// } +/// } +/// } +/// } +/// +/// // In another crate: +/// +/// let mut option = BadOption::new(Box::new(42)); +/// option.change_to_none(); +/// let option2 = option; // Undefined behavior! (pre 1.96) /// ``` /// /// [drop order]: https://doc.rust-lang.org/reference/destructors.html