@@ -22,46 +22,51 @@ let y;
22
22
}
23
23
```
24
24
25
- Each creates its own scope, clearly establishing that one drops before the
26
- other. However, what if we do the following?
25
+ There are some more complex situations which are not possible to desugar using
26
+ scopes, but the order is still defined ‒ variables are dropped in the reverse
27
+ order of their definition, fields of structs and tuples in order of their
28
+ definition. There are some more details about order of drop in [ rfc1875] .
29
+
30
+ Let's do this:
27
31
28
32
``` rust,ignore
29
- let (x, y) = (vec![], vec![]);
33
+ let tuple = (vec![], vec![]);
30
34
```
31
35
32
- Does either value strictly outlive the other? The answer is in fact * no* ,
33
- neither value strictly outlives the other. Of course, one of x or y will be
34
- dropped before the other, but the actual order is not specified. Tuples aren't
35
- special in this regard; composite structures just don't guarantee their
36
- destruction order as of Rust 1.0.
37
-
38
- We * could* specify this for the fields of built-in composites like tuples and
39
- structs. However, what about something like Vec? Vec has to manually drop its
40
- elements via pure-library code. In general, anything that implements Drop has
41
- a chance to fiddle with its innards during its final death knell. Therefore
42
- the compiler can't sufficiently reason about the actual destruction order
43
- of the contents of any type that implements Drop.
36
+ The left vector is dropped first. But does it mean the right one strictly
37
+ outlives it in the eyes of the borrow checker? The answer to this question is
38
+ * no* . The borrow checker could track fields of tuples separately, but it would
39
+ still be unable to decide what outlives what in case of vector elements, which
40
+ are dropped manually via pure-library code the borrow checker doesn't
41
+ understand.
44
42
45
43
So why do we care? We care because if the type system isn't careful, it could
46
44
accidentally make dangling pointers. Consider the following simple program:
47
45
48
46
``` rust
49
47
struct Inspector <'a >(& 'a u8 );
50
48
49
+ struct World <'a > {
50
+ inspector : Option <Inspector <'a >>,
51
+ days : Box <u8 >,
52
+ }
53
+
51
54
fn main () {
52
- let (inspector , days );
53
- days = Box :: new (1 );
54
- inspector = Inspector (& days );
55
+ let mut world = World {
56
+ inspector : None ,
57
+ days : Box :: new (1 ),
58
+ };
59
+ world . inspector = Some (Inspector (& world . days));
55
60
}
56
61
```
57
62
58
- This program is totally sound and compiles today. The fact that ` days ` does
59
- not * strictly* outlive ` inspector ` doesn't matter. As long as the ` inspector `
60
- is alive, so is days.
63
+ This program is totally sound and compiles today. The fact that ` days ` does not
64
+ strictly outlive ` inspector ` doesn't matter. As long as the ` inspector ` is
65
+ alive, so is ` days ` .
61
66
62
67
However if we add a destructor, the program will no longer compile!
63
68
64
- ``` rust,ignore
69
+ ``` rust,compile_fail
65
70
struct Inspector<'a>(&'a u8);
66
71
67
72
impl<'a> Drop for Inspector<'a> {
@@ -70,30 +75,38 @@ impl<'a> Drop for Inspector<'a> {
70
75
}
71
76
}
72
77
78
+ struct World<'a> {
79
+ inspector: Option<Inspector<'a>>,
80
+ days: Box<u8>,
81
+ }
82
+
73
83
fn main() {
74
- let (inspector, days);
75
- days = Box::new(1);
76
- inspector = Inspector(&days);
84
+ let mut world = World {
85
+ inspector: None,
86
+ days: Box::new(1),
87
+ };
88
+ world.inspector = Some(Inspector(&world.days));
77
89
// Let's say `days` happens to get dropped first.
78
90
// Then when Inspector is dropped, it will try to read free'd memory!
79
91
}
80
92
```
81
93
82
94
``` text
83
- error[E0597]: `days` does not live long enough
84
- --> src/main.rs:12:28
95
+ error[E0597]: `world. days` does not live long enough
96
+ --> src/main.rs:20:39
85
97
|
86
- 12 | inspector = Inspector(&days);
87
- | ^^^^ borrowed value does not live long enough
98
+ 20 | world. inspector = Some( Inspector(&world. days) );
99
+ | ^^^^^^ ^^^^ borrowed value does not live long enough
88
100
...
89
- 15 | }
90
- | - `days` dropped here while still borrowed
101
+ 23 | }
102
+ | - `world. days` dropped here while still borrowed
91
103
|
92
104
= note: values in a scope are dropped in the opposite order they are created
93
-
94
- error: aborting due to previous error
95
105
```
96
106
107
+ You can try changing the order of fields or use a tuple instead of the struct,
108
+ it'll still not compile.
109
+
97
110
Implementing ` Drop ` lets the ` Inspector ` execute some arbitrary code during its
98
111
death. This means it can potentially observe that types that are supposed to
99
112
live as long as it does actually were destroyed first.
@@ -116,12 +129,14 @@ sound to drop.
116
129
117
130
The reason that it is not always necessary to satisfy the above rule
118
131
is that some Drop implementations will not access borrowed data even
119
- though their type gives them the capability for such access.
132
+ though their type gives them the capability for such access, or because we know
133
+ the specific drop order and the borrowed data is still fine even if the borrow
134
+ checker doesn't know that.
120
135
121
136
For example, this variant of the above ` Inspector ` example will never
122
137
access borrowed data:
123
138
124
- ``` rust,ignore
139
+ ``` rust,compile_fail
125
140
struct Inspector<'a>(&'a u8, &'static str);
126
141
127
142
impl<'a> Drop for Inspector<'a> {
@@ -130,10 +145,17 @@ impl<'a> Drop for Inspector<'a> {
130
145
}
131
146
}
132
147
148
+ struct World<'a> {
149
+ inspector: Option<Inspector<'a>>,
150
+ days: Box<u8>,
151
+ }
152
+
133
153
fn main() {
134
- let (inspector, days);
135
- days = Box::new(1);
136
- inspector = Inspector(&days, "gadget");
154
+ let mut world = World {
155
+ inspector: None,
156
+ days: Box::new(1),
157
+ };
158
+ world.inspector = Some(Inspector(&world.days, "gadget"));
137
159
// Let's say `days` happens to get dropped first.
138
160
// Even when Inspector is dropped, its destructor will not access the
139
161
// borrowed `days`.
@@ -142,21 +164,26 @@ fn main() {
142
164
143
165
Likewise, this variant will also never access borrowed data:
144
166
145
- ``` rust,ignore
146
- use std::fmt;
147
-
148
- struct Inspector<T: fmt::Display>(T, &'static str);
167
+ ``` rust,compile_fail
168
+ struct Inspector<T>(T, &'static str);
149
169
150
- impl<T: fmt::Display > Drop for Inspector<T> {
170
+ impl<T> Drop for Inspector<T> {
151
171
fn drop(&mut self) {
152
172
println!("Inspector(_, {}) knows when *not* to inspect.", self.1);
153
173
}
154
174
}
155
175
176
+ struct World<T> {
177
+ inspector: Option<Inspector<T>>,
178
+ days: Box<u8>,
179
+ }
180
+
156
181
fn main() {
157
- let (inspector, days): (Inspector<&u8>, Box<u8>);
158
- days = Box::new(1);
159
- inspector = Inspector(&days, "gadget");
182
+ let mut world = World {
183
+ inspector: None,
184
+ days: Box::new(1),
185
+ };
186
+ world.inspector = Some(Inspector(&world.days, "gadget"));
160
187
// Let's say `days` happens to get dropped first.
161
188
// Even when Inspector is dropped, its destructor will not access the
162
189
// borrowed `days`.
@@ -194,16 +221,31 @@ not access any expired data, even if its type gives it the capability
194
221
to do so.
195
222
196
223
That attribute is called ` may_dangle ` and was introduced in [ RFC 1327] [ rfc1327 ] .
197
- To deploy it on the ` Inspector ` example from above, we would write:
224
+ To deploy it on the ` Inspector ` from above, we would write:
225
+
226
+ ``` rust
227
+ #![feature(dropck_eyepatch)]
198
228
199
- ``` rust,ignore
200
229
struct Inspector <'a >(& 'a u8 , & 'static str );
201
230
202
231
unsafe impl <#[may_dangle] 'a > Drop for Inspector <'a > {
203
232
fn drop (& mut self ) {
204
233
println! (" Inspector(_, {}) knows when *not* to inspect." , self . 1 );
205
234
}
206
235
}
236
+
237
+ struct World <'a > {
238
+ days : Box <u8 >,
239
+ inspector : Option <Inspector <'a >>,
240
+ }
241
+
242
+ fn main () {
243
+ let mut world = World {
244
+ inspector : None ,
245
+ days : Box :: new (1 ),
246
+ };
247
+ world . inspector = Some (Inspector (& world . days, " gatget" ));
248
+ }
207
249
```
208
250
209
251
Use of this attribute requires the ` Drop ` impl to be marked ` unsafe ` because the
@@ -279,11 +321,20 @@ attribute makes the type vulnerable to misuse that the borrower
279
321
checker will not catch, inviting havoc. It is better to avoid adding
280
322
the attribute.
281
323
324
+ # A related side note about drop order
325
+
326
+ While the drop order of fields inside a struct is defined, relying on it is
327
+ fragile and subtle. When the order matters, it is better to use the
328
+ [ ` ManuallyDrop ` ] wrapper.
329
+
282
330
# Is that all about drop checker?
283
331
284
332
It turns out that when writing unsafe code, we generally don't need to
285
333
worry at all about doing the right thing for the drop checker. However there
286
334
is one special case that you need to worry about, which we will look at in
287
335
the next section.
288
336
337
+
289
338
[ rfc1327 ] : https://github.com/rust-lang/rfcs/blob/master/text/1327-dropck-param-eyepatch.md
339
+ [ rfc1857 ] : https://github.com/rust-lang/rfcs/blob/master/text/1857-stabilize-drop-order.md
340
+ [ `ManuallyDrop` ] : ../std/mem/struct.ManuallyDrop.html
0 commit comments