Skip to content

Commit e2f6b20

Browse files
authored
Merge pull request #1857 from aochagavia/drop
Stabilize drop order
2 parents 22a9f1a + e21f5b3 commit e2f6b20

File tree

1 file changed

+174
-0
lines changed

1 file changed

+174
-0
lines changed

text/1857-stabilize-drop-order.md

+174
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
- Feature Name: stable_drop_order
2+
- Start Date: 2017-01-19
3+
- RFC PR: https://github.com/rust-lang/rfcs/pull/1857
4+
- Rust Issue: https://github.com/rust-lang/rust/issues/43034
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
I propose we specify and stabilize drop order in Rust, instead of treating
10+
it as an implementation detail. The stable drop order should be based on the
11+
current implementation. This results in avoiding breakage and still allows
12+
alternative, opt-in, drop orders to be introduced in the future.
13+
14+
# Motivation
15+
[motivation]: #motivation
16+
17+
After lots of discussion on [issue 744](https://github.com/rust-lang/rfcs/issues/744),
18+
there seems to be consensus about the need for a stable drop order. See, for instance,
19+
[this](https://github.com/rust-lang/rfcs/issues/744#issuecomment-231215181) and
20+
[this](https://github.com/rust-lang/rfcs/issues/744#issuecomment-231237499) comment.
21+
22+
The current drop order seems counter-intuitive (fields are dropped in FIFO order
23+
instead of LIFO), but changing it would inevitably result in breakage. There have
24+
been cases in the recent past when code broke because of people relying on unspecified
25+
behavior (see for instance the
26+
[post](https://internals.rust-lang.org/t/rolling-out-or-unrolling-struct-field-reorderings/4485)
27+
about struct field reorderings). It is highly probable that similar breakage
28+
would result from changes to the drop order. See for instance, the
29+
[comment](https://github.com/rust-lang/rfcs/issues/744#issuecomment-225918642)
30+
from @sfackler, which reflects the problems that would arise:
31+
32+
> Real code in the wild does rely on the current drop order, including rust-openssl,
33+
and *there is no upgrade path* if we reverse it. Old versions of the libraries will
34+
be subtly broken when compiled with new rustc, and new versions of the libraries
35+
will be broken when compiled with old rustc.
36+
37+
Introducing a new drop order without breaking things would require figuring out how to:
38+
39+
* Forbid an old compiler (with the old drop order) from compiling recent Rust
40+
code (which could rely on the new drop order).
41+
* Let the new compiler (with the new drop order) recognize old Rust code
42+
(which could rely on the old drop order). This way it could choose to either:
43+
(a) fail to compile; or (b) compile using the old drop order.
44+
45+
Both requirements seem quite difficult, if not impossible, to meet. Even in case
46+
we figured out how to meet those requirements, the complexity of the approach would
47+
probably outweight the current complexity of having a non-intuitive drop order.
48+
49+
Finally, in case people really dislike the current drop order, it may still
50+
be possible to introduce alternative, opt-in, drop orders in a backwards
51+
compatible way. However, that is not covered in this RFC.
52+
53+
# Detailed design
54+
[design]: #detailed-design
55+
56+
The design is the same as currently implemented in rustc and is described
57+
below. This behavior will be enforced by run-pass tests.
58+
59+
### Tuples, structs and enum variants
60+
61+
Struct fields are dropped in the same order as they are declared. Consider,
62+
for instance, the struct below:
63+
64+
```rust
65+
struct Foo {
66+
bar: String,
67+
baz: String,
68+
}
69+
```
70+
71+
In this case, `bar` will be the first field to be destroyed, followed by `baz`.
72+
73+
Tuples and tuple structs show the same behavior, as well as enum variants of both kinds
74+
(struct and tuple variants).
75+
76+
Note that a panic during construction of one of previous data structures causes
77+
destruction in a different order. Since the object has not yet been constructed,
78+
its fields are treated as local variables (which are destroyed in LIFO order).
79+
See the example below:
80+
81+
```rust
82+
let x = MyStruct {
83+
field1: String::new(),
84+
field2: String::new(),
85+
field3: panic!()
86+
};
87+
```
88+
89+
In this case, `field2` is destructed first and `field1` second, which may
90+
seem counterintuitive at first but makes sense when you consider that the
91+
initialized fields are actually temporary variables. Note that the drop order
92+
depends on the order of the fields in the *initializer* and not in the struct
93+
declaration.
94+
95+
### Slices and Vec
96+
97+
Slices and vectors show the same behavior as structs and enums. This behavior
98+
can be illustrated by the code below, where the first elements are dropped
99+
first.
100+
101+
```rust
102+
for x in xs { drop(x) }
103+
```
104+
105+
If there is a panic during construction of the slice or the `Vec`, the
106+
drop order is reversed (that is, when using `[]` literals or the `vec![]` macro).
107+
Consider the following example:
108+
109+
```rust
110+
let xs = [X, Y, panic!()];
111+
```
112+
113+
Here, `Y` will be dropped first and `X` second.
114+
115+
### Allowed unspecified behavior
116+
117+
Besides the previous constructs, there are other ones that do not need
118+
a stable drop order (at least, there is not yet evidence that it would be
119+
useful). It is the case of `vec![expr; n]` and closure captures.
120+
121+
Vectors initialized with `vec![expr; n]` syntax clone the value of `expr`
122+
in order to fill the vector. In case `clone` panics, the values produced so far
123+
are dropped in unspecified order. The order is closely tied to an implementation
124+
detail and the benefits of stabilizing it seem small. It is difficult to come
125+
up with a real-world scenario where the drop order of cloned objects is relevant
126+
to ensure some kind of invariant. Furthermore, we may want to modify the implementation
127+
in the future.
128+
129+
Closure captures are also dropped in unspecified order. At this moment, it seems
130+
like the drop order is similar to the order in which the captures are consumed within
131+
the closure (see [this blog post](https://aochagavia.github.io/blog/exploring-rusts-unspecified-drop-order/)
132+
for more details). Again, this order is closely tied to an implementation that
133+
we may want to change in the future, and the benefits of stabilizing it seem small.
134+
Furthermore, enforcing invariants through closure captures seems like a terrible footgun
135+
at best (the same effect can be achieved with much less obscure methods, like passing
136+
a struct as an argument).
137+
138+
Note: we ignore slices initialized with `[expr; n]` syntax, since they may only
139+
contain `Copy` types, which in turn cannot implement `Drop`.
140+
141+
# How We Teach This
142+
[how-we-teach-this]: #how-we-teach-this
143+
144+
When mentioning destructors in the Rust book, Reference and other documentation,
145+
we should also mention the overall picture for a type that implements `Drop`.
146+
In particular, if a `struct`/`enum` implements Drop, then when it is dropped we will
147+
first execute the user's code and then drop all the fields (in the given order). Thus
148+
any code in `Drop` must leave the fields in an initialized state such that they can
149+
be dropped. If you wish to interleave the fields being dropped and user code being
150+
executed, you can make the fields into `Option` and have a custom drop that calls take()
151+
(or else wrap your type in a union with a single member and implement `Drop` such that
152+
it invokes `ptr::read()` or something similar).
153+
154+
It is also important to mention that `union` types never drop their contents.
155+
156+
# Drawbacks
157+
[drawbacks]: #drawbacks
158+
159+
* The counter-intuitive drop order is here to stay.
160+
161+
# Alternatives
162+
[alternatives]: #alternatives
163+
164+
* Figure out how to let rustc know the language version targeted by a given program.
165+
This way we could introduce a new drop order without breaking code.
166+
* Introduce a new drop order anyway, try to minimize breakage by running crater
167+
and hope for the best.
168+
169+
# Unresolved questions
170+
[unresolved]: #unresolved-questions
171+
172+
* Where do we draw the line between the constructs where drop order should be stabilized
173+
and the rest? Should the drop order of closure captures be specified? And the drop order
174+
of `vec![expr; n]`?

0 commit comments

Comments
 (0)