Skip to content

Commit b3b2e92

Browse files
authored
rework may_dangle guidance
1 parent 7861c36 commit b3b2e92

File tree

1 file changed

+34
-7
lines changed

1 file changed

+34
-7
lines changed

src/libs/maintaining-std.md

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,27 @@ Changes to collection internals may affect the order their items are dropped in.
187187

188188
### Is there a manual `Drop` implementation?
189189

190-
Generic types that manually implement `Drop` should consider whether a `#[may_dangle]` attribute is appropriate. The [Nomicon][dropck] has some details on what `#[may_dangle]` is all about.
190+
A generic `Type<T>` that manually implements `Drop` should consider whether a `#[may_dangle]` attribute is appropriate on `T`. The [Nomicon][dropck] has some details on what `#[may_dangle]` is all about.
191191

192-
If the type pretends to store a value that may dangle, but doesn't do so directly (perhaps through `MaybeUninit<T>` or a raw pointer) then make sure there's an appropriate use of `PhantomData` to support dropck. As a [real-world example][rust/issues/76367], adding a `#[may_dangle]` attribute to an `OptionCell<T>` that internally stores its value as a `MaybeUninit<T>` requires both a `PhantomData` marker and a `#[may_dangle]` attribute:
192+
If a generic `Type<T>` has a manual drop implementation that may also involve dropping `T` then dropck needs to know about it. If `Type<T>`'s ownership of `T` is expressed indirectly through pointers, such as `*mut T` or `MaybeUninit<T>`, then `Type<T>` also [needs a `PhantomData<T>` field][RFC 0769 PhantomData] to tell dropck that `T` may be dropped. Types in the standard library that use the internal `Unique<T>` pointer type don't need a `PhantomData<T>` marker field. That's taken care of for them by `Unique<T>`.
193+
194+
As a real-world example of where this can go wrong, consider an `OptionCell<T>` that looks something like this:
195+
196+
```rust
197+
struct OptionCell<T> {
198+
is_init: bool,
199+
value: MaybeUninit<T>,
200+
}
201+
202+
impl Drop<T> for OptionCell<T> {
203+
fn drop(&mut self) {
204+
// Safety: The cell is being dropped, so it can't be accessed again.
205+
unsafe { self.take_inner() };
206+
}
207+
}
208+
```
209+
210+
Adding a `#[may_dangle]` attribute to this `OptionCell<T>` that didn't have a `PhantomData<T>` marker field opened up [a soundness hole][rust/issues/76367] for `T`'s that didn't strictly outlive the `OptionCell<T>`, and so could be accessed after being dropped in their own `Drop` implementations. The `OptionCell<T>` first needed a `PhantomData<T>` field:
193211

194212
```diff
195213
struct OptionCell<T> {
@@ -198,13 +216,21 @@ struct OptionCell<T> {
198216
+ _marker: PhantomData<T>,
199217
}
200218

201-
- impl Drop<T> OptionCell<T> {
202-
+ impl Drop<#[may_dangle] T> OptionCell<T> {
203-
..
204-
}
219+
impl Drop<T> for OptionCell<T> {
205220
```
206221

207-
Types in the standard library that use the internal `Unique<T>` don't need a `PhantomData` marker field. That's taken care of for them by `Unique<T>`.
222+
Then `#[may_dangle]` could be added to the `Drop` implementation:
223+
224+
```diff
225+
struct OptionCell<T> {
226+
is_init: bool,
227+
value: MaybeUninit<T>,
228+
_marker: PhantomData<T>,
229+
}
230+
231+
- impl Drop<T> for OptionCell<T> {
232+
+ unsafe impl Drop<#[may_dangle] T> for OptionCell<T> {
233+
```
208234

209235
### How could `mem` break assumptions?
210236

@@ -277,6 +303,7 @@ Where `unsafe` and `const` is involved, e.g., for operations which are "unconst"
277303
[Forge]: https://forge.rust-lang.org/
278304
[RFC 1023]: https://rust-lang.github.io/rfcs/1023-rebalancing-coherence.html
279305
[RFC 1105]: https://rust-lang.github.io/rfcs/1105-api-evolution.html
306+
[RFC 0769 PhantomData]: https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data
280307
[Everyone Poops]: http://cglab.ca/~abeinges/blah/everyone-poops
281308
[rust/pull/46799]: https://github.com/rust-lang/rust/pull/46799
282309
[rust/issues/76367]: https://github.com/rust-lang/rust/issues/76367

0 commit comments

Comments
 (0)