@@ -22,46 +22,51 @@ let y;
2222}
2323```
2424
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:
2731
2832``` rust,ignore
29- let (x, y) = (vec![], vec![]);
33+ let tuple = (vec![], vec![]);
3034```
3135
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.
4442
4543So why do we care? We care because if the type system isn't careful, it could
4644accidentally make dangling pointers. Consider the following simple program:
4745
4846``` rust
4947struct Inspector <'a >(& 'a u8 );
5048
49+ struct World <'a > {
50+ inspector : Option <Inspector <'a >>,
51+ days : Box <u8 >,
52+ }
53+
5154fn 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));
5560}
5661```
5762
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 ` .
6166
6267However if we add a destructor, the program will no longer compile!
6368
64- ``` rust,ignore
69+ ``` rust,compile_fail
6570struct Inspector<'a>(&'a u8);
6671
6772impl<'a> Drop for Inspector<'a> {
@@ -70,30 +75,38 @@ impl<'a> Drop for Inspector<'a> {
7075 }
7176}
7277
78+ struct World<'a> {
79+ inspector: Option<Inspector<'a>>,
80+ days: Box<u8>,
81+ }
82+
7383fn 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));
7789 // Let's say `days` happens to get dropped first.
7890 // Then when Inspector is dropped, it will try to read free'd memory!
7991}
8092```
8193
8294``` 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
8597 |
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
88100...
89- 15 | }
90- | - `days` dropped here while still borrowed
101+ 23 | }
102+ | - `world. days` dropped here while still borrowed
91103 |
92104 = note: values in a scope are dropped in the opposite order they are created
93-
94- error: aborting due to previous error
95105```
96106
107+ You can try changing the order of fields or use a tuple instead of the struct,
108+ it'll still not compile.
109+
97110Implementing ` Drop ` lets the ` Inspector ` execute some arbitrary code during its
98111death. This means it can potentially observe that types that are supposed to
99112live as long as it does actually were destroyed first.
@@ -116,12 +129,14 @@ sound to drop.
116129
117130The reason that it is not always necessary to satisfy the above rule
118131is 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.
120135
121136For example, this variant of the above ` Inspector ` example will never
122137access borrowed data:
123138
124- ``` rust,ignore
139+ ``` rust,compile_fail
125140struct Inspector<'a>(&'a u8, &'static str);
126141
127142impl<'a> Drop for Inspector<'a> {
@@ -130,10 +145,17 @@ impl<'a> Drop for Inspector<'a> {
130145 }
131146}
132147
148+ struct World<'a> {
149+ inspector: Option<Inspector<'a>>,
150+ days: Box<u8>,
151+ }
152+
133153fn 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"));
137159 // Let's say `days` happens to get dropped first.
138160 // Even when Inspector is dropped, its destructor will not access the
139161 // borrowed `days`.
@@ -142,21 +164,26 @@ fn main() {
142164
143165Likewise, this variant will also never access borrowed data:
144166
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);
149169
150- impl<T: fmt::Display > Drop for Inspector<T> {
170+ impl<T> Drop for Inspector<T> {
151171 fn drop(&mut self) {
152172 println!("Inspector(_, {}) knows when *not* to inspect.", self.1);
153173 }
154174}
155175
176+ struct World<T> {
177+ inspector: Option<Inspector<T>>,
178+ days: Box<u8>,
179+ }
180+
156181fn 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"));
160187 // Let's say `days` happens to get dropped first.
161188 // Even when Inspector is dropped, its destructor will not access the
162189 // borrowed `days`.
@@ -194,16 +221,31 @@ not access any expired data, even if its type gives it the capability
194221to do so.
195222
196223That 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)]
198228
199- ``` rust,ignore
200229struct Inspector <'a >(& 'a u8 , & 'static str );
201230
202231unsafe impl <#[may_dangle] 'a > Drop for Inspector <'a > {
203232 fn drop (& mut self ) {
204233 println! (" Inspector(_, {}) knows when *not* to inspect." , self . 1 );
205234 }
206235}
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+ }
207249```
208250
209251Use 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
279321checker will not catch, inviting havoc. It is better to avoid adding
280322the attribute.
281323
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+
282330# Is that all about drop checker?
283331
284332It turns out that when writing unsafe code, we generally don't need to
285333worry at all about doing the right thing for the drop checker. However there
286334is one special case that you need to worry about, which we will look at in
287335the next section.
288336
337+
289338[ 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