@@ -42,43 +42,186 @@ struct Iter<'a, T: 'a> {
42
42
and that's it. The lifetime will be bounded, and your iterator will be covariant
43
43
over ` 'a ` and ` T ` . Everything Just Works.
44
44
45
- Another important example is Vec, which is (approximately) defined as follows:
45
+ ## Generic parameters and drop-checking
46
+
47
+ In the past, there used to be another thing to take into consideration.
48
+
49
+ This very documentation used to say:
50
+
51
+ > Another important example is Vec, which is (approximately) defined as follows:
52
+ >
53
+ > ``` rust
54
+ > struct Vec <T > {
55
+ > data : * const T , // *const for variance!
56
+ > len : usize ,
57
+ > cap : usize ,
58
+ > }
59
+ > ```
60
+ >
61
+ > Unlike the previous example , it * appears * that everything is exactly as we
62
+ > want . Every generic argument to Vec shows up in at least one field .
63
+ > Good to go!
64
+ >
65
+ > Nope .
66
+ >
67
+ > The drop checker will generously determine that `Vec <T >` does not own any values
68
+ > of type T . This will in turn make it conclude that it doesn 't need to worry
69
+ > about Vec dropping any T 's in its destructor for determining drop check
70
+ > soundness . This will in turn allow people to create unsoundness using
71
+ > Vec 's destructor .
72
+ >
73
+ > In order to tell the drop checker that we * do * own values of type T , and
74
+ > therefore may drop some T 's when * we * drop , we must add an extra `PhantomData `
75
+ > saying exactly that :
76
+ >
77
+ > ```rust
78
+ > use std :: marker;
79
+ >
80
+ > struct Vec <T > {
81
+ > data : * const T , // *const for variance!
82
+ > len : usize ,
83
+ > cap : usize ,
84
+ > _owns_T: marker :: PhantomData <T >,
85
+ > }
86
+ > ```
87
+
88
+ But ever since [RFC 1238 ](https : // rust-lang.github.io/rfcs/1238-nonparametric-dropck.html),
89
+ * * this is no longer true nor necessary ** .
90
+
91
+ If you were to write :
46
92
47
93
```rust
48
94
struct Vec <T > {
49
- data : * const T , // *const for variance!
95
+ data : * const T , // ` *const` for variance!
50
96
len : usize ,
51
97
cap : usize ,
52
98
}
99
+
100
+ # #[cfg(any())]
101
+ impl <T > Drop for Vec <T > { /* … */ }
53
102
```
54
103
55
- Unlike the previous example, it * appears* that everything is exactly as we
56
- want. Every generic argument to Vec shows up in at least one field.
57
- Good to go!
104
+ then the existence of that ` impl<T> Drop for Vec<T> ` makes it so Rust will consider
105
+ that that ` Vec<T> ` _ owns_ values of type ` T ` (more precisely: may use values of type ` T `
106
+ in its ` Drop ` implementation), and Rust will thus not allow them to _ dangle_ should a
107
+ ` Vec<T> ` be dropped.
108
+
109
+ ** Adding an extra ` _owns_T: PhantomData<T> ` field is thus _ superflous_ and accomplishes nothing** .
110
+
111
+ ___
112
+
113
+ But this situation can sometimes lead to overly restrictive code. That's why the
114
+ standard library uses an unstable and ` unsafe ` attribute to opt back into the old
115
+ "unchecked" drop-checking behavior, that this very documentation warned about: the
116
+ ` #[may_dangle] ` attribute.
58
117
59
- Nope.
118
+ ### An exception: the special case of the standard library and its unstable ` #[may_dangle] `
60
119
61
- The drop checker will generously determine that ` Vec<T> ` does not own any values
62
- of type T. This will in turn make it conclude that it doesn't need to worry
63
- about Vec dropping any T's in its destructor for determining drop check
64
- soundness. This will in turn allow people to create unsoundness using
65
- Vec's destructor.
120
+ This section can be skipped if you are only writing your own library code; but if you are
121
+ curious about what the standard library does with the actual ` Vec ` definition, you'll notice
122
+ that it still needs to use a ` _marker: PhantomData<T> ` field for soundness.
66
123
67
- In order to tell the drop checker that we * do * own values of type T, and
68
- therefore may drop some T's when * we * drop, we must add an extra ` PhantomData `
69
- saying exactly that :
124
+ < details >< summary >Click here to see why</ summary >
125
+
126
+ Consider the following example :
70
127
71
128
``` rust
72
- use std :: marker;
129
+ fn main () {
130
+ let mut v : Vec <& str > = Vec :: new ();
131
+ let s : String = " Short-lived" . into ();
132
+ v . push (& s );
133
+ drop (s );
134
+ } // <- `v` is dropped here
135
+ ```
136
+
137
+ with a classical ` impl<T> Drop for Vec<T> { ` definition, the above [ is denied] .
138
+
139
+ [ is denied ] : https://rust.godbolt.org/z/ans15Kqz3
140
+
141
+ Indeed, in this case we have a ` Vec</* T = */ &'s str> ` vector of ` 's ` -lived references
142
+ to ` str ` ings, but in the case of ` let s: String ` , it is dropped before the ` Vec ` is, and
143
+ thus ` 's ` ** is expired** by the time the ` Vec ` is dropped, and the
144
+ ` impl<'s> Drop for Vec<&'s str> { ` is used.
145
+
146
+ This means that if such ` Drop ` were to be used, it would be dealing with an _ expired_ , or
147
+ _ dangling_ lifetime ` 's ` . But this is contrary to Rust principles, where by default all
148
+ Rust references involved in a function signature are non-dangling and valid to dereference.
149
+
150
+ Hence why Rust has to conservatively deny this snippet.
151
+
152
+ And yet, in the case of the real ` Vec ` , the ` Drop ` impl does not care about ` &'s str ` ,
153
+ _ since it has no drop glue of its own_ : it only wants to deallocate the backing buffer.
154
+
155
+ In other words, it would be nice if the above snippet was somehow accepted, by special
156
+ casing ` Vec ` , or by relying on some special property of ` Vec ` : ` Vec ` could try to
157
+ _ promise not to use the ` &'s str ` s it holds when being dropped_ .
158
+
159
+ This is the kind of ` unsafe ` promise that can be expressed with ` #[may_dangle] ` :
160
+
161
+ ``` rust ,ignore
162
+ unsafe impl <#[may_dangle] 's > Drop for Vec <& 's str > { /* … */ }
163
+ ```
164
+
165
+ or, more generally:
166
+
167
+ ``` rust ,ignore
168
+ unsafe impl <#[may_dangle] T > Drop for Vec <T > { /* … */ }
169
+ ```
170
+
171
+ is the ` unsafe ` way to opt out of this conservative assumption that Rust's drop
172
+ checker makes about type parameters of a dropped instance not being allowed to dangle.
173
+
174
+ And when this is done, such as in the standard library, we need to be careful in the
175
+ case where ` T ` has drop glue of its own. In this instance, imagine replacing the
176
+ ` &'s str ` s with a ` struct PrintOnDrop<'s> /* = */ (&'s str); ` which would have a
177
+ ` Drop ` impl wherein the inner ` &'s str ` would be dereferenced and printed to the screen.
178
+
179
+ Indeed, ` Drop for Vec<T> { ` , before deallocating the backing buffer, does have to transitively
180
+ drop each ` T ` item when it has drop glue; in the case of ` PrintOnDrop<'s> ` , it means that
181
+ ` Drop for Vec<PrintOnDrop<'s>> ` has to transitively drop the ` PrintOnDrop<'s> ` s elements before
182
+ deallocating the backing buffer.
183
+
184
+ So when we said that ` 's ` ` #[may_dangle] ` , it was an excessively loose statement. We'd rather want
185
+ to say: "` 's ` may dangle provided it not be involved in some transitive drop glue". Or, more generally,
186
+ "` T ` may dangle provided it not be involved in some transitive drop glue". This "exception to the
187
+ exception" is a pervasive situation whenever ** we own a ` T ` ** . That's why Rust's ` #[may_dangle] ` is
188
+ smart enough to know of this opt-out, and will thus be disabled _ when the generic parameter is held
189
+ in an owned fashion_ by the fields of the struct.
190
+
191
+ Hence why the standard library ends up with:
192
+
193
+ ``` rust
194
+ # #[cfg(any())]
195
+ // we pinky-swear not to use `T` when dropping a `Vec`…
196
+ unsafe impl <#[may_dangle] T > Drop for Vec <T > {
197
+ fn drop (& mut self ) {
198
+ unsafe {
199
+ if mem :: needs_drop :: <T >() {
200
+ /* … except here, that is, … */
201
+ ptr :: drop_in_place :: <[T ]>(/* … */ );
202
+ }
203
+ // …
204
+ dealloc (/* … */ )
205
+ // …
206
+ }
207
+ }
208
+ }
73
209
74
210
struct Vec <T > {
75
- data : * const T , // *const for variance!
211
+ // … except for the fact that a `Vec` owns `T` items and
212
+ // may thus be dropping `T` items on drop!
213
+ _owns_T: core :: marker :: PhantomData <T >,
214
+
215
+ ptr : * const T , // `*const` for variance (but this does not express ownership of a `T` *per se*)
76
216
len : usize ,
77
217
cap : usize ,
78
- _marker : marker :: PhantomData <T >,
79
218
}
80
219
```
81
220
221
+ </details >
222
+
223
+ ___
224
+
82
225
Raw pointers that own an allocation is such a pervasive pattern that the
83
226
standard library made a utility for itself called ` Unique<T> ` which:
84
227
0 commit comments