Skip to content

Commit 8e0398c

Browse files
committed
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.
1 parent 3c6f982 commit 8e0398c

File tree

1 file changed

+27
-11
lines changed

1 file changed

+27
-11
lines changed

src/libcore/mem/mod.rs

+27-11
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,26 @@ pub use crate::intrinsics::transmute;
6969
/// ```
7070
///
7171
/// 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.:
72+
/// up in unsafe or FFI code. For example:
73+
///
74+
/// ```
75+
/// use std::mem;
76+
///
77+
/// let mut v = vec![65, 122];
78+
/// // Build a `String` using the contents of `v`
79+
/// let s = unsafe { String::from_raw_parts(v.as_mut_ptr(), 2, v.capacity()) };
80+
/// // immediately leak `v` because its memory is now managed by `s`
81+
/// mem::forget(v);
82+
/// assert_eq!(s, "Az");
83+
/// // `s` is implicitly dropped and its memory deallocated.
84+
/// ```
85+
///
86+
/// The above is correct, but brittle. If code gets added between the construction of
87+
/// `String` and the invocation of `mem::forget()`, a panic within it will cause a double
88+
/// free because the same memory is handled by both `v` and `s`. This can be fixed by
89+
/// storing the result of `v.as_mut_ptr()` in a local variable and calling `mem::forget()`
90+
/// before `String::from_raw_parts`. This kind of issue can be more robustly prevented by
91+
/// using [`ManuallyDrop`], which is usually preferred for such cases:
7492
///
7593
/// ```
7694
/// use std::mem::ManuallyDrop;
@@ -88,16 +106,14 @@ pub use crate::intrinsics::transmute;
88106
/// // `s` is implicitly dropped and its memory deallocated.
89107
/// ```
90108
///
91-
/// Using `ManuallyDrop` here has two advantages:
109+
/// `ManuallyDrop` robustly prevents double-free because we disable `v`'s destructor
110+
/// before doing anything else. `mem::forget()` doesn't allow this because it consumes its
111+
/// argument, forcing us to call it only after extracting anything we need from `v`.
92112
///
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.
113+
/// Note that the above code cannot panic between construction of `ManuallyDrop` and
114+
/// building the string. But even if it could (after a modification), a panic there would
115+
/// result in a leak and not a double free. In other words, `ManuallyDrop` errs on the
116+
/// side of leaking instead of erring on the side of dropping.
101117
///
102118
/// [drop]: fn.drop.html
103119
/// [uninit]: fn.uninitialized.html

0 commit comments

Comments
 (0)