Skip to content

Commit 5d39517

Browse files
authored
Rollup merge of #69618 - hniksic:mem-forget-doc-fix, r=RalfJung
Clarify the relationship between `forget()` and `ManuallyDrop`. As discussed on reddit, this commit addresses two issues with the documentation of `mem::forget()`: * The documentation of `mem::forget()` can confuse the reader because of the discrepancy between usage examples that show correct usage and the accompanying text which speaks of the possibility of double-free. The text that says "if the panic occurs before `mem::forget` was called" refers to a variant of the second example that was never shown, modified to use `mem::forget` instead of `ManuallyDrop`. Ideally the documentation should show both variants, so it's clear what it's talking about. Also, the double free could be fixed just by placing `mem::forget(v)` before the construction of `s`. Since the lifetimes of `s` and `v` wouldn't overlap, there would be no point where panic could cause a double free. This could be mentioned, and contrasted against the more robust fix of using `ManuallyDrop`. * This sentence seems unjustified: "For some types, operations such as passing ownership (to a funcion like `mem::forget`) requires them to actually be fully owned right now [...]". Unlike C++, Rust has no move constructors, its moves are (possibly elided) bitwise copies. Even if you pass an invalid object to `mem::forget`, no harm should come to pass because `mem::forget` consumes the object and exists solely to prevent drop, so there no one left to observe the invalid state state.
2 parents f4c675c + 2bebe8d commit 5d39517

File tree

1 file changed

+49
-16
lines changed

1 file changed

+49
-16
lines changed

src/libcore/mem/mod.rs

+49-16
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ pub use crate::intrinsics::transmute;
5858
///
5959
/// # Examples
6060
///
61-
/// Leak an I/O object, never closing the file:
61+
/// The canonical safe use of `mem::forget` is to circumvent a value's destructor
62+
/// implemented by the `Drop` trait. For example, this will leak a `File`, i.e. reclaim
63+
/// the space taken by the variable but never close the underlying system resource:
6264
///
6365
/// ```no_run
6466
/// use std::mem;
@@ -68,9 +70,40 @@ pub use crate::intrinsics::transmute;
6870
/// mem::forget(file);
6971
/// ```
7072
///
71-
/// The practical use cases for `forget` are rather specialized and mainly come
72-
/// up in unsafe or FFI code. However, [`ManuallyDrop`] is usually preferred
73-
/// for such cases, e.g.:
73+
/// This is useful when the ownership of the underlying resource was previously
74+
/// transferred to code outside of Rust, for example by transmitting the raw
75+
/// file descriptor to C code.
76+
///
77+
/// # Relationship with `ManuallyDrop`
78+
///
79+
/// While `mem::forget` can also be used to transfer *memory* ownership, doing so is error-prone.
80+
/// [`ManuallyDrop`] should be used instead. Consider, for example, this code:
81+
///
82+
/// ```
83+
/// use std::mem;
84+
///
85+
/// let mut v = vec![65, 122];
86+
/// // Build a `String` using the contents of `v`
87+
/// let s = unsafe { String::from_raw_parts(v.as_mut_ptr(), v.len(), v.capacity()) };
88+
/// // leak `v` because its memory is now managed by `s`
89+
/// mem::forget(v); // ERROR - v is invalid and must not be passed to a function
90+
/// assert_eq!(s, "Az");
91+
/// // `s` is implicitly dropped and its memory deallocated.
92+
/// ```
93+
///
94+
/// There are two issues with the above example:
95+
///
96+
/// * If more code were added between the construction of `String` and the invocation of
97+
/// `mem::forget()`, a panic within it would cause a double free because the same memory
98+
/// is handled by both `v` and `s`.
99+
/// * After calling `v.as_mut_ptr()` and transmitting the ownership of the data to `s`,
100+
/// the `v` value is invalid. Even when a value is just moved to `mem::forget` (which won't
101+
/// inspect it), some types have strict requirements on their values that
102+
/// make them invalid when dangling or no longer owned. Using invalid values in any
103+
/// way, including passing them to or returning them from functions, constitutes
104+
/// undefined behavior and may break the assumptions made by the compiler.
105+
///
106+
/// Switching to `ManuallyDrop` avoids both issues:
74107
///
75108
/// ```
76109
/// use std::mem::ManuallyDrop;
@@ -80,24 +113,24 @@ pub use crate::intrinsics::transmute;
80113
/// // does not get dropped!
81114
/// let mut v = ManuallyDrop::new(v);
82115
/// // Now disassemble `v`. These operations cannot panic, so there cannot be a leak.
83-
/// let ptr = v.as_mut_ptr();
84-
/// let cap = v.capacity();
116+
/// let (ptr, len, cap) = (v.as_mut_ptr(), v.len(), v.capacity());
85117
/// // Finally, build a `String`.
86-
/// let s = unsafe { String::from_raw_parts(ptr, 2, cap) };
118+
/// let s = unsafe { String::from_raw_parts(ptr, len, cap) };
87119
/// assert_eq!(s, "Az");
88120
/// // `s` is implicitly dropped and its memory deallocated.
89121
/// ```
90122
///
91-
/// Using `ManuallyDrop` here has two advantages:
123+
/// `ManuallyDrop` robustly prevents double-free because we disable `v`'s destructor
124+
/// before doing anything else. `mem::forget()` doesn't allow this because it consumes its
125+
/// argument, forcing us to call it only after extracting anything we need from `v`. Even
126+
/// if a panic were introduced between construction of `ManuallyDrop` and building the
127+
/// string (which cannot happen in the code as shown), it would result in a leak and not a
128+
/// double free. In other words, `ManuallyDrop` errs on the side of leaking instead of
129+
/// erring on the side of (double-)dropping.
92130
///
93-
/// * We do not "touch" `v` after disassembling it. For some types, operations
94-
/// such as passing ownership (to a function like `mem::forget`) requires them to actually
95-
/// be fully owned right now; that is a promise we do not want to make here as we are
96-
/// in the process of transferring ownership to the new `String` we are building.
97-
/// * In case of an unexpected panic, `ManuallyDrop` is not dropped, but if the panic
98-
/// occurs before `mem::forget` was called we might end up dropping invalid data,
99-
/// or double-dropping. In other words, `ManuallyDrop` errs on the side of leaking
100-
/// instead of erring on the side of dropping.
131+
/// Also, `ManuallyDrop` prevents us from having to "touch" `v` after transferring the
132+
/// ownership to `s` - the final step of interacting with `v` to dispoe of it without
133+
/// running its destructor is entirely avoided.
101134
///
102135
/// [drop]: fn.drop.html
103136
/// [uninit]: fn.uninitialized.html

0 commit comments

Comments
 (0)