@nikomatsakis I just wrote the detailed design part and would appreciate your feedback before submitting a PR.
Detailed design
The design is the same as currently implemented in rustc and is described below. This behavior will be enforced by run-pass tests.
Structs and enum variants
Struct fields are dropped in the same order as they are declared. Consider, for instance, the struct below:
struct Foo {
bar: String,
baz: String,
}
In this case, bar will be the first field to be destroyed, followed by baz.
Tuple structs show the same behavior, as well as enum variants of both kinds (struct and tuple variants).
Note that a panic during construction of one of previous data structures causes destruction in reverse order. Since the object has not yet been constructed, its fields are treated as local variables (which are destroyed in LIFO order). See the example below:
let x = MyStruct {
field1: String::new(),
field2: String::new(),
field3: panic!()
};
In this case, field2 is destructed first and field1 second, which may seem counterintuitive at first but makes sense when you consider that the initialized fields are actually temporary variables.
Slices and Vec
Slices and vectors show the same behavior as structs and enums. This behavior can be illustrated by the code below, where the first elements are dropped first.
for x in xs { drop(x) }
Again, if there is a panic during construction of the slice or the Vec, the drop order is reversed (that is, when using [] literals or the vec![] macro).
Allowed unspecified behavior
Besides the previous constructs, there are other ones that do not need a stable drop order (at least, there is not yet evidence that it would be useful). It is the case of vec![expr; n] and closure captures.
Vectors initialized with vec![expr; n] syntax clone the value of expr in order to fill the vector. In case clone panics, the values produced so far are dropped in unspecified order. The order is closely tied to an implementation detail and the benefits of stabilizing it seem small. It is difficult to come up with a real-world scenario where the drop order of cloned objects is relevant to ensure some kind of invariant. Furthermore, we may want to modify the implementation in the future.
Closure captures are also dropped in unspecified order. At this moment, it seems like the drop order is similar to the order in which the captures are consumed within the closure (see this blog post for more details). Again, this order is closely tied to an implementation that we may want to change in the future, and the benefits of stabilizing it seem small. Enforcing drop order through closure captures seems like a terrible footgun at best (the same effect can be achieved with much less obscure methods, like passing a struct as an argument).
Note: we ignore slices initialized with [expr; n] syntax, since they may only contain Copy types, which in turn cannot implement Drop.