Stabilize drop order

Rendered

cc @nrc @pnkfelix

Reserved first comment

@nagisa I like your idea and I think it would be a great follow-up RFC!

@nagisa I definitely agree with you that writing a dtor of this style is best practice. But I'd also like to hear an affirmative case for why we should change the drop order -- as a rule of thumb, I think that execution order should be well-defined unless there is a strong reason for it not to be, and drop code seems to me to be not so different from any other code that executes. Put another way, changing drop order seems to me to be primarily a vector by which we can surprise people and cause bugs in their code. (It doesn't, e.g., affect performance all that much, and if it did, people could re-order their fields to accommodate that.) Is there an example of why we might want to change the drop order?

@nagisa As written, if you replace union with struct, then your impl<T> ManuallyDropped<T> will error with "cannot move out of borrowed content" on
unsafe fn manually_drop(&mut self) { drop(self.value) }

You need to call drop_in_place and repopulate the field with a value that is safe to drop a second time.  An enum can avoid this second drop like in https://github.com/cesarb/clear_on_drop/issues/3#issuecomment-272724588 not sure if that's what your union does.

I very specifically mentioned it is a prototype. Also known as sketch or “I didn’t compile or run it, but am sure the idea is implementable with roughly this code”.

I really don't have enough experience, so please correct me if it makes no sense :).
Should drop order be defined?
I like @nikomatsakis words "I think that execution order should be well-defined unless there is a strong reason for it not to be".
Are there any advantages to having the drop order be unspecified? I can think of three, but all not very strong. (1) Having a random destruction order would force code that relies on it, to be explicit and have a custom destructor. (2) It may allow some performance optimizations (but as @nikomatsakis says, people could just re-order their fields if performance was a problem). (3) In the struct field reordering discussion, it was brought up that a bit of randomness could be a small security advantage. Can the same be said about destruction order?
What happens if the drop order is reversed?
This is one of the alternatives mentioned in the rfc.
I think that if the drop order is changed, every crate that wants to compile with both a nightly and stable version will have to follow @nagisa's method to manually specify the drop order. That will basically make all crates that rely on a specific drop order use a custom dtor. This will continue to be so until rustc versions with the old drop order become obsolete. So changing the drop order would only makes less crates depend on it.
What are the advantages of reversing the drop order? To me it seems the only advantage is to make teaching it a little bit easier. Just like all variables are dropped in reverse order of when they are defined, so are the fields of structs, tuples etc (with the notes about custom dtors and panics during construction, which this rfc explains very nice). But I like a certain simplicity...
How great are the consequences of this rfc?
The difficult thing with this rfc is of course that there are a few creates relying on something that is currently unspecified. So there is the right to make a breaking change. But because this is something that can not be statically checked for, some code may silently break (if there are not enough test in a crate).
Is there any guess about how many crates would be affected by a change? Is it more like 10, of more like 100? Or are we in danger of spending more time thinking about it than testing and fixing a few crates would take?
Overall I am slightly in favor of this rfc. Changes just don't really seem worth it.
But reversing the drop order, or maybe even randomizing it together with explicit destructors is a close second.

Could there be an associated pragma for specifying the drop order? That way the crates in question would just have to do something like the following on the structs that require it and they would be good to go. This way they could be specific AND forwards compatible.
#[drop_order(fifo)]
struct MySpecialSnowflake {
    ...
}

We could even have a #[drop_order(random)] if people thought it could help with security in any way.
Hopefully a crator run (with unit tests) could help tell which (essential) libraries need to be updated, and they could be updated before the changes were released.

@vitiral Unfortunately, adding an attribute to specify drop order would cause problems when compiling the old version of the library with a recent compiler. In that case, there would be nothing in the code indicating the preferred drop order and the program could break in subtle ways. Therefore, we need to at least determine that the default behavior should be what we have now. Opt-in drop orders (possibly in the form of attributes) can be added later on.

@vitiral Unfortunately, adding an attribute to specify drop order would cause problems when compiling the old version of the library with a recent compiler

Add the thing that lets people choose the drop order.
Wait two releases (3 months) until it reaches the stable channel.
Change the default drop order.

@briansmith at that point the introduced complexity is not worth it anymore IMO (that is why I wrote the RFC)

@ncm I can't tell if your comment is supporting a defined drop order or not =)

I still feel like no one has proposed a concrete advantage to arbitrary drop orders, right? I think it's mainly "maybe one will happen in the future"?

@nikomatsakis

I still feel like no one has proposed a concrete advantage to arbitrary drop orders, right?

If "dropping in in-memory order" aka "order after field reordering" can be considered arbitrary, then "no pessimization by default" is the concrete advantage.
If Rust ABI is defined then it will stop being arbitrary and will become defined as well.
Structures can be quite large especially if they are auto-generated and/or contain other nested structs. Who knows what benefits "optimization-by-default" can bring occasionally, it's better to give it chance.
Regarding "you can manually reorder if performance bottleneck is found", the whole point of these layout optimizations is that they accumulate, they affect each and every struct by default. You may not know anything about caches or prefetch and you'll still benefit from them.
I find it reasonable for all the field-order-related features to work together on the common goal -  "optimization-by-default" rather than to interfere with each other.

I am in strongly in favor of some amount of nailing down initialization order.
That order need not match the physical layout-order of members in a struct (presuming the compiler is allowed to re-arrange those, as petrochenkov seems to suggest).  C++ inherited from C that physical layout must match member lexical declaration order; it equated that order to initialization order, but not for any especially good reason.  Any specified order of both initialization and destruction should be easily visible to the programmer, matching some prominent lexical order.  In C++ the lexical order of supplying arguments to initializers does not affect order of initialization, but every compiler warns if it doesn't match the other three.
The lexical order of initializers in the source code should match the temporal order in which they are run.  It doesn't seem necessary for that order to match layout order, or to match member declaration order, or for those two to match. But, if there are two places where the same members are initialized, those lexical orders should be enforced to match. That might leave the order of default-initialized members unspecified, but it seems to suffice that if you care about the order, you must mention the members whose order matters; any not mentioned could be allowed to be initialized in any order, or even in parallel with one another and with mentioned members.  If the compiler can determine that two mentioned initializations don't interact, it can rearrange or interleave them just like it can rearrange statements in a block, by the as-if rule.
Elements in array-like objects probably should be initialized in index order.  Again, of course, if the compiler can prove that a program can't tell what the order was, the compiler is free to re-order or do them in parallel.

The reason why different explicit initializations have to match one another's sequence is that Drop can't know which of possible sequences was used.  If Drop cannot know anything about the initialization sequences, then it might need to rely on the lexical order of declaration of the members; in that case, explicit initializations would need to match that order, although layout could still be independent.

Also: My reasons for wanting some initialization order determinacy have nothing to do with performance.  They are about correctness, and implicit dependency.
That anyone could reliably predict the performance effect of any given change to initialization order strikes me as absurd.  The only way to know if it has any effect is to measure, but there's no way to guess how portable the result is even between two apparently identical machines, or under different loads on the same machine.

@ncm
Long story short: you are assuming too many things that are true for C++ to be true for Rust as well, while they are not.
First, the initialization order is already defined and, unlike in C++, it's left-to-right order of fields in struct initializer expression, regardless of field order in struct definition. This RFC is only about drop order.
Second, unlike in C++, the drop order doesn't have to be tied to initialization order. Moreover, it can't be tied to it because the initialization order can vary and isn't known in general.
Third, unlike in C++, fields cannot depend on each other during construction and immediately after it. All the dependencies are introduced while the object is alive and their order is arbitrary and isn't tied to initialization order in any way.
Fourth, like in C++, if user introduces dependencies between fields, he needs to rely of destruction order. However, specifying and documenting it explicitly is much better than implicitly relying on whatever compiler provides.

Thank you, petrochenkov.  I should have read more carefully above before posting.
The one place remaining that I do not see addressed (please correct me if I missed it) is what happens when one member initialization fails: will already-initialized members be dropped in reverse order? I think it matters because it is not only dependency between members that can require such an order; the members may hold external resources that need it.

@ncm Before initialization is complete, the structure doesn't exist yet, so the rules are (or should be?) identical to calls (which don't happen until after their arguments are fully evaluated).

Third, unlike in C++, fields cannot depend on each other during construction and immediately after it.

There's a way to do this with some indirection and global mutable context.
let s = S { owning_ptr_to_arena: create_arena(&context), ptr_to_object_in_arena: create_object(&context) };

context should be mutable through a cell or unsafe pointer.
create_arena should store a non-owning copy of owning_ptr_to_arena, e.g. raw pointer, in context.
create_object uses this raw pointer to the arena to create objects in it.
In this setup 1) initialization order is correct by definition and not tied to field order in struct definition 2) drop order in case of panic is correct because s is not constructed yet and temporaries are destroyed in reverse 3) drop order for fields in a complete structure is still important and needs to be relied upon, like in the general case. So, everything seems good.

I want to make clear up front: My preference is for a semantics based on the source text; not in-memory order after potentially arbitrary reordering.
But I'm going to start off attempting an analogy with order-of-evaluation in another context. (Perhaps this will be torn apart as a strawman, but I think the analogy is valid.)

@petrochenkov

If "dropping in in-memory order" aka "order after field reordering" can be considered arbitrary, then "no pessimization by default" is the concrete advantage. [...] Who knows what benefits "optimization-by-default" can bring occasionally, it's better to give it chance.

I have seen similar arguments put forward to support unspecified evaluation order for the expressions used in function call arguments. That particular lack of specification has always been of interest to me because it is one property that is shared, oddly enough, by both the C and Scheme standards (at least up through r6rs; not sure about r7rs).
Furthermore: I have even seen concrete evidence presented of an unspecified evaluation order of call arguments. In particular, a post from 1995 to the comp.compilers newsgroup. (Disclaimer: I used to work on Larceny and Will Clinger was my Ph.D advisor.)
http://compilers.iecc.com/comparch/article/95-08-080

IEEE/ANSI Scheme allows the compiler to re-order certain expressions that may contain side effects. Both Twobit and Chez Scheme do this as an optimization. As compiled by Twobit for the SPARC, this optimization reduces the code size for Larceny v0.24 from 489456 to 452872 bytes, a savings of 7.5%.

(Quoting to ensure the relevant paragraph remains even if link goes dead.)
A report of a 7.5% code size reduction is nothing to sneeze at.

I personally suspect that the 7.5% figure deserves qualification: Its not clear how much of that code size win could be achieved via more-sophisticated semantics-preserving optimization of the same code base under some defined evaluation order.

So, okay, that's my attempt to point out that unspecified evaluation order for certain language forms can indeed yield performance wins (at least in certain contexts).
Nonetheless: My subjective opinion is that it is far more important to Rust's own goals that a human reader can predict what a piece of code will do (especially if that code never uses an unsafe block).
So for something like drop order, I would prefer that we specify some reasonable evaluation order that is based on the source text.
If the compiler is rearranging structural representation and that causes a change to the drop evaluation order, then I think that is in conflict with the goal of human predictability.

If tying down a source-based drop evaluation order causes us to leave significant performance on the table, then I would choose to invest in tooling (e.g. dynamic instrumentation) that could advise the programmer to reorder their fields due to drop performance.

then I would choose to invest in tooling (e.g. dynamic instrumentation) that could advise the programmer to reorder their fields due to drop performance

Wouldn't that assume there is a one optimal order for all architectures and use-cases?

then I would choose to invest in tooling (e.g. dynamic instrumentation) that could advise the programmer to reorder their fields due to drop performance

Wouldn't that assume there exists an optimal order for all architectures and use-cases?

Architecture-dependence for optimal performance is problem at all levels of programming, not just something like drop order.
For example, see discussion in this paper (pdf); they point out in section 4 of the paper that you have to select entirely different algorithms depending on whether you are targeting a Pentium IV or a Pentium III.

citation: "Overcoming the Memory Wall in Symbolic Algebra: A Faster Permutation Algorithm", G. Cooperman and X. Ma, Communications in Computer Algebra (CCA -- SIGSAM Bulletin) 36(4), pp. 1--4, 2002.

Anyway: No, I am not presuming that there exists a single ordering that will be optimal for all architectures and use cases. I am assuming that any cost in execution time here will pale in comparison to the benefit of having predictable semantics.
(I am also assuming that a developer who really wants to attempt to extract optimal performance will be able to either 1. provide manual drop implementations or 2. multiple structure definitions, under different cfg switches, that provide the optimal ordering for each target architecture.)

I previously cited my advisor's post to comp.compilers earlier in this thread; however, looking over the comp.compiler's postings now, I do think that David Chase's many messages there are echoing the kind of attitude that I have here today. Consider e.g. this:

I'm also a little amazed to be encountering such resistance to a well-defined order of evaluation in this decade. The last three places that I've worked, people talk about "quality this" and "reliability that" and about the importance of development procedures, and things like ISO 9000. I've talked to people who work for other companies, and they discuss various interesting things that they do with defect-tracking to ensure that products ship with few bugs. One important part of bug tracking and bug fixing is reproducible behavior (if nothing else, the less things can vary, the less you have to test for a given amount of reliability). I've even heard people discuss hardware and instruction-set architecture changes to increase the determinism of program execution. I've heard of people producing applications for which the most important things are reliability and time-to-market -- they ship debuggable binaries, giving up a factor of 2 in performance (at least). You'd think that this sort of thing was REALLY IMPORTANT, much more than some apparently insignificant, sometimes-used, never-measured option to optimize code.

That one is especially funny given it starts out saying "in this decade", and the message is at this point over twenty years old.

https://github.com/rust-lang/rust/issues/23222 has an interesting issue relating to drop order that this RFC may want to take a position on.

@steveklabnik Thanks for the link to the issue. This is expected behavior: if a panic occurs during array construction, there is actually no array at the moment. Therefore, the fields are dropped according to the rules for local variables (LIFO).

@petrochenkov

Structures can be quite large especially if they are auto-generated and/or contain other nested structs. Who knows what benefits "optimization-by-default" can bring occasionally, it's better to give it chance.

So, I find this argument well-reasoned, though I remain ... dubious, the same as @pnkfelix. On the one hand, I see an analogy with the #[repr] -- we claim the right to change your representation by default, so why not reserve that same right for your drop order? And yet the two don't feel entirely analogous -- in particular the representation is not order of execution. In general, we have the goal of a fully defined order-of-execution. So in a sense there are two conflicting precedents here.
Also, the argument that "we should do drop order as an in-memory stride because that will be faster" seems a bit suspicious to me. Drop is just one particular bit of code that accesses fields in the structs -- there is lots more code that the user has written that is using those structs, and yet we claim that we will reorder your fields without analyzing that code at all to understand its usage patterns and this will yield a more efficient result. If this performance effect is so important, surely we would want to leave it in the hands of the user, who understands the access patterns?
(That said, I agree that in the one case we can control, dropping in memory order seems to be strictly more efficient, even if I suspect the effect is still quite small.)
To accommodate the problem of large structs that ought to be dropped in memory order, we can of course just suggest that the user use #[repr(linear)] (not yet supported...) in order to give themselves control of the memory layout. Then they can reorder the fields in the order that they would prefer for them to be dropped and maximizes their overall efficiency.

Trying to tune the language design to match present architectures is a losing game.  If the language is any kind of success, it will persist over dozens of generations of abstract-machine realization, through changes we can scarcely imagine.  Machines are constructed to try to make the programs people already have run faster, but have always been delivered tuned to the previous generation of applications; meanwhile, the compilers tune the code they generate to favor the design choices of a previous generation of machines.  It's co-evolution following chaotic trajectories.
It is a paradoxical fact of computing that the hardware changes radically from one year to the next, but the (supposedly flexible) software stays the same over decades, only very gradually being supplanted by entirely new applications.  Every prediction of the future of hardware has turned out way off, often even opposite to, what we got, usually for reasons that seem obvious, but only in hindsight.  Lately, we are seeing investment in on-die FPGAs, but they might turn out to have been a mistake, and disappear, possibly not because of anything wrong with the idea, but just because the compilers never learn to use them properly.   Similarly, the compilers' capabilities will certainly change, maybe adopting deep-learning methods to produce optimal code despite apparently limiting language semantic requirements.
So when we talk about something like order of operations, the most valuable quality will be predictability, not just so that programmers can reason about it, but also so that machine designers have a target to optimize, and that compilers can be successfully programmed to reason about.  (By analogy, internet trunk routers have astonishingly complex internal queuing systems that interact with neighboring routers in incomprehensible ways, but a router chain that cannot be modeled from the outside as a simple queue obliterates throughput just because end-to-end flow control is tuned to that model, and cannot adapt to actual router details.)
My main concern, now, is about cases where dependence relations cannot be perceived by the compiler or the machine because the relations exist outside the program, or even outside the whole machine; program resources often must track the states of entities, even physical objects, in other processes or other equipment.  Usually, in these cases, the only dependence relationship that can be assumed is temporal: Y might depend on X (and by induction, on W, V, U, ...) because Y was created after X (and after W, V, etc.).  When the programmer has no opportunity to code that dependency, such as with a container owned by another subsystem, she must rely on the runtime following a predictable pattern that matches the most common relationship, thus (for our case) dropping array elements in the reverse of the common order of creation.
It's easy to conceive of a system where the elements in an array were -not- created in index order, so a programmer who needs to depend on a sensible default drop order must ensure that the creation order actually does end up matching index order; but that is rarely difficult, and is most usually the simplest way to code.
So this is a vote for simple, LIFO reverse-index default drop order at least in uniform containers like arrays.

@ncm so, this is a different argument -- specifically, you are advocating for two things:

fixing the drop order
changing the drop order from what we currently do to adopt a uniform "reverse order of creation"

I agree with you on having the drop order be defined and predictable, but it seems like the current order -- inconsistent as it is -- still suffices to allow programmers who want control to have it. I guess I don't see the benefit in breaking existing code here, is the bottom line.

@rfcbot fcp merge
I'd like to move that we merge this RFC. The RFC proposes that we document the current drop order and commit to not changing it. The counterarguments raised thus far in the thread are as follows:

We should leave the order undefined, because it's bad style to rely on it and because we may want to change it if we change the representation so as to optimize (e.g., 1 and 2)

Users can control drop order manually if desired by overriding Drop (and maybe using union).
Counterarguments:

Predictability matters more
Users can still optimize by reordering fields
There is existing code that relies on the current drop order

We should adopt a more uniform order than the one we currently have, and in particular drop in reverse order of construction

Counterarguments:

There is existing code that relies on the current drop order
In some cases, such as arrays, dropping in-order has been shown to be empirically faster, albeit in microbenchmarks.

For me personally, the bottom line is the following:
(a) I have a preference for defining execution order
(b) I know existing code relies on this
(c) I don't see any strong benefits to changing the existing order nor to leaving it undefined

cc @rust-lang/compiler I'd like input from compiler team folk too.

Niko: Sorry, I don't agree that programmers always have enough control to enforce a reverse order, where it is needed, without undue pain. Often enough you are handed an array and have no say in its creation or destruction.
Existing code depending on a presently unspecified, but de-facto forward order suggests not that the present order is preferable -- the code was written to what the system does, whatever that is -- but only that the default order really does matter.  We could learn more by examining the cases that depend on forward order and see what extra work they did to adapt to it, vs. the reverse policy; and identify other cases where code has been obliged to enforce its own reverse order, doing extra work could have avoided.  I'm betting that you will find more cases of jimmying things to work with the de-facto order than cases that depend on it as is.
Then we have the question of interoperability: C++ arrays drop in reverse order.  There is plenty to complain about in C++, and people love complaining about it (sometimes even mistakenly), but over two decades I have never encountered a complaint about that policy.

tl;dr: Something so fundamental deserves deeper consideration than whim and historical accident.
If we don't find production C++ code that deliberately subverts the default (reverse) destruction order in performance-sensitive cases, that calls into question the notion that there is performance advantage to avoiding it.

but over two decades I have never encountered a complaint about that policy.

If we don't find production C++ code that deliberately subverts the default (reverse) destruction order in performance-sensitive cases, that calls into question the notion that there is performance advantage to avoiding it.

I’m pretty sure it signifies the fact that nobody cares much either way more rather than anything else.

I agree with @nagisa that people tend to not care about the drop order that much. I've fielded all sorts of questions from people regarding what Rust does and does not specify and I've never been asked about the order fields are dropped in a struct or array.
I'm fine with stabilising the current behaviour, especially given the difficulty of changing it without breaking existing code.

I'm very interested in drop order being stabilized as it would help greatly in my redesign of rental. If, however, rust ever were to one day gain a built-in ability to express self-referential structs, reverse drop order would be highly preferable in that case. Forward drop order means the most dependent field would need to come first, which is counter to intuition and the order of borrowing as normally expressed in stack frames.
Forward drop order is still far preferable to no drop order, though, and I could simply provide an option to automatically reverse the fields in the proc macro to get intuitive layout back.

If you have not been asked, it is likely because nobody seriously believed a language intended to supplant C++ in industrial use might break with long-established and well reasoned practice on an idle whim. (I admit astonishment.)  This choice is the first such consequential instance I have found.  It makes me wonder what else I have missed.
I can assure you that engineers using C++ in serious industrial applications are deeply aware of when and in what order their destructors run.  It matters most when actual physical systems are being operated. There may not be any industrial processes controlled by a Rust program yet, but millions of peoples' lives depend every day on C++ programs. If you find that as terrifying to contemplate as you should, know it is those engineers' attention to detail that keeps their systems out of news headlines.

@ncm could you clarify how "let's not subtly break large portions of existing Rust codebases" is "an idle whim"?

@ncm so you're saying that these highly detail-oriented and careful C++ programmers will just assume that Rust works the same way as C++? Seems like a contradiction. It seems like having a known order is more important than what that order is and, as @sfackler points out, it's not clear that breaking existing Rust code is worth changing the current behaviour.

If, however, rust ever were to one day gain a built-in ability to express self-referential structs, reverse drop order would be highly preferable in that case. Forward drop order means the most dependent field would need to come first, which is counter to intuition and the order of borrowing as normally expressed in stack frames.

To be clear, it would only matter if fields holding a reference access it during their Drop implementation, no?

There isn't enough Rust code written yet for there to be large anything.  When you have a billion lines of Rust we can begin talking about when something large might turn up.
Every program that depends on the explicitly-unspecified drop order started out broken.  Correct Rust programs don't depend on drop order.

Drop order was left explicitly unspecified for reasons.  Counting programs that depend, regardless, on the accident of implementation is putting your thumb on the scale.  It gives veto power over reasoned choice to programmers who deliberately wrote bad code.
Aatch: Industrial programmers are not using Rust.  They are using a language with a defined, natural drop order. If it were not right, we would know.

withoutboats: Internal memory dependence is not the only kind of dependence.

There isn't enough Rust code written yet for there to be large anything. When you have a billion lines of Rust we can begin talking about when something large might turn up.

Do you think that diminishing the importance of existing Rust codebases is going to help you win over the people you are trying to convince?

Every program that depends on the explicitly-unspecified drop order started out broken. Correct Rust programs don't depend on drop order.

Correct C programs pre-C99 didn't type pun through unions, and yet many did anyway. Sometimes things become de-facto stabilized.

Drop order was left explicitly unspecified for reasons.

Really - what reasons were those? I seem to remember it not being much more than no one thought to specify them before 1.0.

withoutboats: Internal memory dependence is not the only kind of dependence.

I was responding to a comment about self-referential structs, which is about internal memory dependence. That drop order matters outside of that is demonstrated by the fact that users are already relying on the existing drop order & I don't need to be reminded.
More broadly @ncm I have to admit that your comments have had the opposite impact on me of what you seem to intend. Your rhetoric is bombastic, melodramatic, value-laden, and honestly ill-fitted to a technical discussion. That I can see you have not put forward any argument about the technical superiority of any drop order or another, though you've made a variety of specious and fallacious arguments about one order being "natural" and "not complained about" or that a language which uses it is widespread "in industry."
The argument that users from C++ would have to learn that Rust uses a different drop order has been made. The argument that changing the drop order from the current drop order would cause churn for some existing users has also been made. What other technical differences exist between the two drop orders under consideration?

If Rust achieves the aims of its creators, there really will be billions of lines of Rust code.  "Billions of lines" is not bombast, but literal fact in the present, for C++, and in a very conceivable near future, for Rust. I would like to see that future. Thinking in terms of billions of lines of code, and what it takes to make it all happen and work, is educational and sobering, or should be. It makes acting to favor a handful of erroneously coded programs at the expense of an uncounted number of correctly coded programs (and not even counting, or checking them) seem deeply irresponsible.
There is no problem with programmers learning Rust's drop ordering, however it ends up. Programmers learn to work around infelicities.  The problem is with writing code that interacts with the outside world, as industrially useful programs do, and being obliged, regularly, to work around a drop order that does not match how things in that world work.  There is no question but that it can be done: existing (correct) Rust programs do it when they need to. C doesn't even have anything like a "drop", yet C coders make do, and usually get it right, or close enough.
What effect does the need to work around the language order have on designs?  Do programs that need a reverse drop-order end up avoiding using the "drop" mechanism, and instead rely on hand-coded cleanup, as in C?  Or must they use special containers that define a different drop order? Or something else? Has anyone looked?
Language design mistakes rarely sink a language.  Algol gave us null pointers, Tony Hoare's "billion-dollar mistake".  C++ constructors, by default, are also implicit conversions.  The mistakes' total cost grows with the amount of code, with no upper bound. It is worth some up-front effort to avoid simple mistakes that would cost forever after.

@ncm Your arguments make some sense in the abstract, but could you give us an example of a scenario where an industrial program interacts with the outside world, where reverse-order destruction is appropriate and forward-order is not? Just sketch it out. I think what many in this thread (including myself) are missing is why, specifically, industrial programs would favor a reverse order.
[For what it's worth, I'm also inclined towards to the reverse order, and I was afraid it would come to this the last time it was decided to punt on the question, but it's also not clear to me how much of a difference it makes.]

@ncm I continue to find little enlightenment in your comment. Frankly most of what you've written is condescending, paternalistic & off-topic. Assume a well informed audience & please desist in communicating this way; it is very rude.
As @glaebhoerl also comments, I do not understand the reason a user would need to "work around" the drop order. If you control the Drop impl, you control the struct definition, and can simply reorder it. The struct definition is not connected to initialization order (as has been explained already on this thread).
The only case I can imagine a user needing to work around the drop order, instead of simply controlling it, is when a user needs repr(linear), but also needs the drops to occur in a non-linear (possibly reverse) order. But I see no reason to believe a user that any particular drop order is more likely to run into this issue than any other. Does anyone actually know of any example?

glaebhoerl asked for an example.
This example uses components and processes that I hope are familiar, standing in for what would not be.  Before I lay out the example, let me reiterate my concerns: this is not about dropping members in a struct; Drop order in structs was resolved to everybody's satisfaction long ago.  It is about the default drop order in arrays, lists, and other sequential containers.  I further reiterate that my concern is not about what it is possible to learn, or to code; programmers daily work around many different mistakes, often their own. My concern is about the cost of writing and maintaining code that would otherwise not need to exist at all, writing variations over and over again, and maintaining them as long as software lasts, throughout the life of the language. My example uses database operations, but they stand in for other processes less easily to present; bringing up database details would only distract from the point.  Finally, I apologize for repeating myself, perhaps too much; I am trying to bring everyone along.
I begin with a capsule review of the database semantics the example relies on, with apologies to those who don't need it.  Databases support transactions, defined as a start, then a sequence of data operations -- reads, writes, in any order -- followed by a commit. Before the commit, the transaction is open, and nothing has actually been changed in the database.  At any time before the commit, the transaction may be rolled back, and nothing sticks.  The commit might fail, for external reasons, and then nothing sticks.  Some databases support sub-transactions, with their own start, ops, and commit, mixed into the reads and writes of the first, outermost one; and sub-sub-transactions, that affect what the next transaction out sees.  If any one of those is still open when the outer transaction tries to commit, the whole transaction fails, and likewise down the line.  There might be many top-level transactions open at once, each with sub-transactions, and when the work is done, they all need to commit in the right order.
It's natural to think of starting a transaction as making an object, and committing as dropping it.  It's not hard to imagine data structures to keep track of the relationships of all the transactions at every level, and corresponding algorithms to traverse the data structures, committing everything in the right order. But look: if, at each "start", you put a transaction id on a list, then at the end you can just walk back, committing each id when you get to it. When you reach the beginning, everything has been committed in the right order.  That is what I mean when I say "natural order".  No data structures, no algorithms, no fancy traversals, just "pop/commit, pop/commit, ...".  The reason it works is that the absolute order defines a dependency graph that contains, as subsets, all possible actual dependency relationships.
You might object that nobody would operate a database that way, but recall that this is an example demonstrating the principle as clearly and simply as seemed possible.  (Also: they do.) You might have noticed a similar, albeit simpler process as control flow in your program returns from deep in the call graph, retiring arguments and local variables along the way.  (Algol-60 got that right. I didn't invent the expression "natural order".)
So here I repeat my questions from before: What effect does the need to work around the language order have on designs? Do programs that need a reverse drop-order end up avoiding using the "drop" mechanism, and instead rely on hand-coded cleanup, as in C? Or must they use special containers that define a different drop order? Or something else? Has anyone looked?

@rfcbot concern internal-references-future-proofing
@jpernst makes two claims in a comment, contingent on if Rust is ever extended to allow internal references within structures (aka "self-referential structs"), then:

claim 1: the current forward drop order would require people to order their fields so that references come before the target (because the self-references might be wrapped in values that override Drop and access the referenced data in their destructors), and
claim 2: it is more natural for the most dependent field to come last, analogously to how things are ordered in stack frames.

I want to highlight this point because it is not captured in the current discussion summary.
Regarding the two claims
I agree with the first claim, at least in terms of how the struct definition is written, unless some strange exceptions were made as part of adding support for self-referential structures.
I interpret the second claim to only be about struct literal expressions, not struct definitions. It is worth noting that the field ordering for non-tuple struct literals is not constrained by the definition order. So I think the second claim is really only about tuple struct literals where one does not use feature(relaxed_adts) to initialize the fields via their numeric id's.
So... the second claim is arguably about a somewhat narrow class of syntax construction, that one should be able to "reasonably" workaround by the time this hypothetical self-referential structure feature were to land.
Admission: I have in the past dismissed this concern (see comment from issue #744)), but with more recent discussion of immovable values, I've become more optimistic that we might actually add self-referential structures at some point, and so I'd like to make sure we explicitly acknowledge the drawback.

@pnkfelix I agree that @jpernst's point is interesting, but I'm not sure why you write this:

I interpret the second claim to only be about struct literal expressions, not struct definitions. I

I interpreted the point as being that one would not be able to do something like this:
struct Foo { // here let 'self be an "internal" lifetime
    arena: Arena<'self>,
    data: &'self i32
}

and instead one would have to write:
struct Foo {
    data: &'self i32, // points into the arena
    arena: Arena<'self>,
}

However, there do seem to be a lot of variables here. For one thing, it's unclear if we will ever support such "existential" lifetimes, though I would like to do so. One could imagine the declaration syntax for such a construct naturally giving an ordering between the "arena" and the "pointers into the arena" (indeed, such an ordering might be needed to help us figure when to "open" the existential). But even in the absence of that, as @withoutboats pointed out, we permit cycles unless one of the types involved has a drop implementation. (Note that while "immovable types" may be a part of this story, they themselves do not permit such "self-referential" data-structures; in particular, the goal of "encapsulating" the lifetime like that is to allow a Foo instance to be moved while keeping its references intact, and immovable types obviously don't support that.)
Still, I think ultimately that @jpernst's objection is the same as @ncm's objection -- that it is natural to think about later things "referring back into" earlier things, and we should orient around that consistently, because it will work out most conveniently. I can believe this, but it does have to be weighed against trying to make changes at this juncture.

As an aside, It's interesting that @ncm has been talking a lot about containers. I hadn't thought of containers as being in the scope of this RFC per se -- if they are, it seems like @rust-lang/libs ought to be giving "official input" as well. That said, it seems obvious that we would want the drop order of containers to harmonize with the language (e.g., [T; n] and Vec<T> ought to drop in the same order), and it makes sense to consider the whole set together. Interestingly, only Vec seems to have a drop-order that is particularly aligned with "creation order". Neither HashMap nor BTreeMap keeps any record of the order in which items were inserted. So really it seems we are only talking about vectors.

To that end, I think a simplified form of @ncm's scenario is this:

You have some code that is using local variables and which, on panic, has some recovery code in dtors.
You rewrite that code to use a vector to put your stack in the heap in place of using recursion and local variables.
Now your code stops working.

Of course, one can easily address this by having some newtype'd vector (or perhaps a Stack type) that has an overloaded Drop, but @ncm's point (I believe) is that this is a wart, and we might as well avoid it now.
This has to be weighed against the price of breaking existing code. I do not count that as lightly as @ncm does, despite my agreement that the amount of rust code now is small compared to how much code I hope there will be. 😄

No conclusions here, just thoughts.

On the libs side, I have always felt a bit uncomfortable relying on any drop order, even if it was specified - you could just mess up initially and stick the fields in the wrong order, or do it right the first time and then have someone come through later and reorder fields to "clean up" and cause badness that may not be noticed initially. Using ManuallyDrop to enforce the ordering explicitly seems like a more reliable approach approach to me.

@nikomatsakis wrote:

I'm not sure why you write this:

I interpret the second claim to only be about struct literal expressions, not struct definitions.

I guess I left too much unspecified in that comment. So I'll elaborate (this is what I deserve for registering a concern, I guess...)
I think it is debatable whether struct Foo { data: &'self i32, arena: Arena<'self>, } is significantly less intuitive than struct Foo { arena: Arena<'self>, data: &'self i32, }; that question may very well be what  @jpernst was referencing, but that's not how I interpreted it, since it strikes me as a relatively minor issue. (Data structures often have cross-cutting constraints between their fields, and the "natural order of presentation" in the struct declaration may be subject to a lot of interpretation.)
The reason that I distinguished between struct literal expressions and struct definitions was because  I expect to be more natural in such a literal to write the initializer expression for the arena first, and then the initializer expression for the data field.
Otherwise you end up with something like this (again assuming the hypothetical language extension to somehow support internal references):
Foo { data: <need a reference here ...>, arena: <... to something allocated from here>, }

You would need to create the arena before you create Foo, and move it into place without invalidating the reference held within data.
I had instead assumed that one would do something like this for the literal:
Foo { arena: <place the arena into its immovable home>, data: <allocate value within placed arena> }

(of course that assumes some extension to allow one field-initializer expression to reference the values held in previously initiialzed fields of the same struct. But that seems like a natural extension to assume when discussing internal references, right?)

@pnkfelix I frankly don't see the point of using struct initializers and having strange features for them, I'd rather just initialize it using assignment expressions on individual fields.
As for the declaration, this seems fine (btw, @nikomatsakis, reminder that Arena<'a> is unworkable):
struct Foo {
    a: &'arena i32,
    arena: Arena,
    b: &'arena i32,
}

@eddyb oh sure, that's fine for things that don't implement Drop.

@ncm has pointed out that C++ uses reverse destruction order.
Another data point, mostly just to show that this choice is not the one obvious thing: Swift uses in-order destruction for both arrays and struct fields.
For exaple, this Swift code:
import Cocoa;

class D {
    let i: Int;
    init(i: Int) { self.i = i }
    deinit { print(NSString(format: "Deinit D(%d)", self.i)) }
}

class Pair {
    let x: D; let y: D;
    init(x: D, y: D) { self.x = x; self.y = y; }
    deinit {
        print(NSString(format: "Deiniting Pair(D(%d), D(%d))", self.x.i, self.y.i));
    }
}

func demo() {
    var array = [D]();
    array.append(D(i: 1));
    array.append(D(i: 2));

    let p = Pair(x: D(i: 3), y: D(i: 4));
    print(NSString(format: "done with demo, P(%d, %d)", p.x.i, p.y.i));
}

demo();

prints:
done with demo, P(3, 4)
Deiniting Pair(D(3), D(4))
Deinit D(3)
Deinit D(4)
Deinit D(1)
Deinit D(2)

(hat tip to kballard for pointing this out years ago when we were first wrestling with this question.)

(Putting my moderator hat on)
The conversation in this thread has gotten pretty heated at times.
While I wouldn't label any of the comments (from any party) as violating the code of conduct, I want to ensure that our community remains welcoming to all, regardless of their background and technical experience (either in Rust or in other programming languages).

So, everyone:

Try to put your best self forward,
Recognize that passion (either your own or that of others) for a topic can sometimes yield hyperbolic narratives that grate against other people, regardless of where objective truth lies,
Pointing out that someone's style of argument is nonconstructive is definitely permitted by the CoC, but please try always to presume good intent from the arguer, and finally,
Remember the fourth bullet from the CoC: every design choice has trade-offs.

And of course, if you do feel like the Rust CoC is being violated, either here or elsewhere in our community, I encourage you to e-mail<EMAIL_ADDRESS>

I'm afraid Niko's summary utterly fails to capture why a forward default drop order in arrays and similarly sequential data structures would cause sometimes-prohibitively expensive problems for a large class of economically important applications.  The reason the summary fails is that important state outside the addressable memory of the program is essential to the problem.  I admit I am flummoxed that this distinction has been so hard to convey. Can some kind reader who understands why the distinction seems so far out of reach help explain?
As an accommodation for the applications at issue, it would suffice for the compiler to have a command-line switch like -fdrop=reverse, that would be obeyed by native arrays and by the standard List, Vec, and other sequential types, and (by convention) other similar structures.  (I agree with Niko that "forward" and "reverse" have no meaning in the context of hashes and trees.)  I find it hard to conceive of a class of applications that would benefit noticeably from a -fdrop=forward option, but I concede its possibility, in principle.  The default could be left as -fdrop=adaptive, not promising any particular order.
But given that applications seem to divide neatly, almost, into "needs reverse" and "don't care" bins, the overwhelmingly simpler alternative would be to default to reverse order for everybody, as has proved universally satisfactory for for three+ decades of C++.

Do we have any concrete examples of Rust code that would break or statistics on how much might break if the drop order were to change? I've seen dozens of assertions that it would be terrible but not a single citation so far, which is starting to worry me.
nagisa's original comment at the very top of this thread (https://github.com/rust-lang/rfcs/pull/1857#issuecomment-273774983) pretty much covers how I've felt the entire time. It doesn't make sense to me to stabilize drop order before giving code that depends on a certain drop order a way to explicitly specify it, unless there's very clear evidence that the code breakage would be unacceptable, and simply telling people to fix their broken code (after providing a way to fix it) would also be unacceptable.

@ncm I think we can all agree that applications divide neatly into "don't care" and "needs a specific order". The part that I'm not getting and I think no one else is getting is why "needs a specific order" is necessarily equivalent to "needs reverse order". As far as I know (even after reading all of your comments), the only special thing about reverse order is 1) it's what C++ does, 2) it's what must be done for local variables on the stack, which are good arguments that reverse drop order for structs/arrays would be more consistent/less surprising (and why I'd also favor it). But because Rust allows struct initialization order to be totally different from struct field order, I don't see why a non-reverse drop order would make Rust impractical for serious industrial software, as you appear to be arguing. If anything, I'd say Rust isn't usable for such applications yet because it doesn't have a way to manually specify the intended drop order, which is what every program that cares should be doing.

The reason the summary fails is that important state outside the addressable memory of the program is essential to the problem.

I don't see the connection. If a Rust struct/array contains a bunch of references to memory it doesn't fully control that must be dropped in a certain way (is that not what you're talking about?), it can still just reorder things to get the drop order it wants, right? (once the default order is specified or a way to manually specify it is provided)

@Ixrec: Thank you for your careful response.  I agree that it is not possible to make a sensible determination about the effect of a drop-order change on the mentioned programs without examining them.  In particular: is there any difficulty in correcting erroneous programs that depend on the (unspecified but implemented) forward drop order of the current compiler?  I.e., is it anything fundamental to the design, or were they just coded to the order the author found in the implementation, and would have been coded slightly differently were it otherwise? What other (correct) programs do not depend on any drop order, but could be substantially simpler if permitted to depend on a reverse default drop order?
I have already had my nose rubbed in how difficult it is for readers here to perceive the significance of state outside the computer. (A physical world exists, independently of software, with parts that sometimes interact with software. Yourself, for example.) As I said, I don't know how to express it any more clearly, and I hope someone else has some insight that would allow them to explain it better, because it is central to the immediate question. I can at least emphasize, again, that this is specifically not about "a bunch of references to memory".  If it were even tangentially about references to memory, it would be so much easier to convey.
The practical equivalence of "need a specific order" and "need reverse order" is very evidently not obvious to everyone.  However, it is irreducible fact that uncountably many C++ programs that directly affect each of us every day do rely on reverse destruction order, for relentlessly practical reasons, while programs that need expensive workarounds because there is no such thing as "forward destruction order" to be had have not surfaced in decades of industrial use.  This is not accidental (as, they say, Rust's de-facto forward order was). C++ was created, first and foremost, as an industrial language. Its first production use was controlling telephone network switches, among the most demanding jobs of the time.  If it ever had a forward-destruct-order release, it would have been sent back for repairs long before the outside world ever saw it.  In other words, computer science did not demand reverse destruction order, industrial reality did.  If Rust is ever to take its place doing the industrial jobs dominated by C++ today, it needs to be correctly equipped.  C++ didn't need selectable destruction order to take over the world.  It only needed reverse (i.e. natural) order.
Will future space probes, heart pacemakers, continental electrical grids, and nanobot swarms be programmed in Rust?  Other languages seem adequate to count ad clicks and animate paperclips.

@Ixrec a couple that come to mind:
https://github.com/sfackler/rust-openssl/blob/master/openssl/src/ssl/mod.rs#L1694
https://github.com/rust-lang/crates.io/blob/4bffbfb1a3014a192b9e87fe4a1c332e5bd44ed7/src/db.rs#L104

@sfackler Thanks, those are pretty convincing examples.

The only case I can imagine a user needing to work around the drop order, instead of simply controlling it, is when a user needs repr(linear), but also needs the drops to occur in a non-linear (possibly reverse) order. But I see no reason to believe a user that any particular drop order is more likely to run into this issue than any other. Does anyone actually know of any example?
Even if this doesn't motivate any particular default order, I think it argues for some way to control the drop order that doesn't feel like a hack, as manual dropping does.  This goes especially if self-referencing structs become supported, which would make drop-order-sensitive structures both more common and more likely to appear in entirely safe code, whereas manual dropping currently depends on unsafe code.  (Note that if an &move-based replacement for the Drop trait is ever added, it would allow safe manual dropping in some cases, but the borrow checker would still need a fixed field order to determine lifetimes in self-referencing structs.)
Either that, or there should be a way to explicitly specify how fields are ordered in memory, separately from their order in the source.  One or the other ought to be decoupled.

On self-referencing structs: supporting them without unsafe{} usage would already require that the compiler can validate the drop order is safe. At the point where it is able to do that, it may (seems like it should be, anyhow) be able to determine a valid drop order.
And if they require some unsafe{}, then using ManuallyDrop-like methods seems reasonable.
Because of this, I don't necessarily see the potential for self-referencing structs as a strong argument for a specified drop order [this is modulo potential desires about keeping a "refers-back-to" lexical ordering].

@sfackler : If I understand them correctly, those are extremely unconvincing examples.
Making either program compatible with any specified default drop order would take just a few-line patch -- i.e., their dependence is 0% fundamental, 100% incidental. Under the current spec, they are just erroneous for omitting the trivial code to enforce the struct field drop order they need.  After being fixed they would be unaffected by any specified or even randomized default drop order.  Are those really the most persuasive examples?
Specifying a reverse drop order for native arrays and other sequential containers would, it appears, have no effect on these programs.

@ncm
I'm sorry, I've been trying to give you the benefit of the doubt, but I find this line of argumentation quite unconvincing! We are all aware that running a destructor can have side-effects on the real world, and that C++ is a successful language which uses a consistent reverse-order.
You state the following:

programs that need expensive workarounds because there is no such thing as "forward destruction order" to be had have not surfaced in decades of industrial use.

It's true that this may be because reverse order is clearly superior, but it could equally well be because the specifics of what order you use are not that important -- most programs don't care, after all -- and in any case never require anything more than a local workaround.
I do think that you made a valid argument that, when order matters, reverse drop order in vecs is more likely to be correct (but far from guaranteed). In particular because, when using a vec as a stack, it is most common to want to drop the most recently pushed item, as you said. But it still only requires a fairly simple local fix (use a different type for your stack). This doesn't seem like it would be an "expensive" workaround, although if it went unnoticed, it could certainly cause an expensive bug down the line (we have ample demonstration that humans are bad at considering edge cases, after all, and so it is easy to overlook that drop order is significant in any particular case).
Nonetheless, I guess there is another side to your argument that I did not capture, which I would summarize as "if ain't broke, why fix it?" That is, C++ seems to have found reverse drop order for vectors fairly useful, so why would we do otherwise? Fair enough, except that there are short-term costs to changing, so it seems fair to request a more positive demonstration of downsides to the current setup.
I guess a key question is how bad it would be to change the drop order. My feeling is that this is a dangerous change to make: it's a "silent" change that just affects behavior in subtle ways. Any such change risks making people angry and generating bad press. This may in turn harm the adoption of Rust (due to a reputation for making subtle behavioral changes). It seems to me we should reserve such changes for when they seem most important.

@Niko: Thank you.  I do not expect to have any more comments on this RFC.

Thanks for the RFC, @aochagavia! I've tried to quickly read over the comments here and on the issue, and have a couple of notes.
First, you talk about how panics induce LIFO order. But, AIUI, this is LIFO order given the way you ordered the initializer, not in terms of the struct definition. (Of course, for tuples and arrays you don't have a choice.) So even if we chose to drop in LIFO order of struct declaration, that would still result in some counterintuitive cases around panics. In other words, I'm not sure a completely intuitive/unsurprising model is even possible here.
To push on that point a little further: is there any concern about coupling drop order and field layout order, when that does become specified (or in repr cases that are already specified)?
It's also slightly weird for field order to become significant for drop, but not for layout -- in particular I could imagine wanting to tie these together for cache locality reasons (as has been discussed). But since they are coupled for arrays etc, I think in practice there't not really a concern here.
In the end, I pretty much agree with the bottom of @nikomatsakis's FCP post.

@aturon Thanks for your comment. I think we agree. It is true that the LIFO order depends on the initializer and I think it should remain so. I just pushed a change to make this clearer.

@ncm

As a policy matter, it should be troubling to everyone that all it takes to force a definition is to write a few programs that depend on an accidental choice in the sole implementation.

The Rust community tends to make nuanced decisions, rather than follow black-and-white dictums. It's true that we left drop order unspecified and hence are "within our rights" to change it and break code. But even in such cases, we attempt a careful cost/benefit analysis. This has already been substantially litigated on the thread, but the basic consensus at the moment is that people simply don't see enough benefit to changing the order to outweigh the cost of breakage.

You could cause a tuple to destruct in  reverse initialization order like this, no?
let b = Woof::new();
let a = Meow::new();
(a,b,panic!())

It's not soo complex that, if you want a particular destruction order during initialization, then you'll must order the initializers that way, while if you want a particular destruction order after initialization, then you must order the struct itself backwards to that.
I suppose structs could benefit from a #[reverse_doc_order] attribute that merely lists the struct's fields in reverse order in the documentation, which fields pun make fairly ergonomic.  It's tricker for tuples including tuple structs but you can give an example that shows it being constructed backwards like the above.

I said I wouldn't be contributing further to this discussion, and I am not writing this to change anyone's mind, but I am at the ISO C++ Standard meeting in Kona, and this thread has been the source of no little merriment and slack-jawed wonderment, with a touch of sadness.
When I told Bjarne (without mentioning Rust) that someone had suggested the destruction order of C++ arrays was a historical accident, and hardly any users cared one way or the other, he was frankly incredulous.
I spoke with someone who, with his team, had implemented a production operating system kernel in a proprietary language with Rust-like pointer borrowing, where its freedom from data races was a essential. He just shook his head: "It's a shame, Rust was an interesting experiment."
I confess I still hope Rust can be more than that, but I know good luck does not last, and cleverness flags, and then what can you rely on?

Quite honestly, your post is so incredible that, well, I don't believe it.  I don't suggest bad faith, but as stated I can only imagine there is some context left out, or else that you unwittingly misrepresented the situation with Rust.
Maybe there's a confusion between arrays and structures?  C++ allows class fields to freely reference each other even during construction, with minimal checking, and hence cares quite a lot about construction and destruction order.  The situation with Rust is a bit different, since currently self-references are not allowed at all, and if they are allowed in the future, the borrow checker will prevent most mistakes.  Also keep in mind that there's a difference between "most people, if asked for their thoughts, would have no preference" and "most people have rarely or never written code which would be affected by it".  I think most people here agree that reverse destruction is theoretically better; it's just that in Rust it would be only a tiny bit better, not enough to justify breaking changes.

Could any of these C++ standards committee members be persuaded to make a post here explaining why having a different array destruction order from C++ would be disastrous?
The reason we haven't been persuaded by your previous posts is that you never really gave a technical reason why reverse destruction order was better (or when you tried, it was so vague and surrounded by ethos/pathos that no one could make sense of it). If everyone on the C++ standards committee really is aware of a good technical reason not to do this, that nobody else on this thread is aware of, at least one of them should be able to explain what that reason is.

@comex: Denial is the first stage; take as much time as you need.  For the rest, if you can't be bothered to read what I already wrote, why write more?  Come back when you've bothered. (Free hints: 1. Arrays, not members.  2. Pointers are irrelevant.)
@Ixrec: I have done more work than I could afford. Somebody who has reasons to care will step up to do the research, or won't.  The project will fall in a hole, or won't.
This won't be Rust's last crisis.  It needs process to recognize and deal with crises, because good luck and cleverness flag. The project can choose irrelevance at any time. Almost all do. (Dancing paperclips await.)
I wish the project enough luck to develop what it needs to carry on without.

In my opinion if you're doing unsafe things that rely on drop order you should be using ManuallyDrop anyway to explicitly drop things the way you need it. Thus I don't really care what the drop order is.

I would also be interested in hearing from a C++ committee member, if this is truly their opinion.  Points:

C doesn't have destructors, and is widely used in industry.  If drop order matters to the point of breaking a language even if that language itself has ways for you to do it in cases where it matters, why is C so popular?

If I am building an industrial application wherein failure can literally kill someone, why in the world am I silently relying on implicit language behavior?  Why am I not using a dedicated zero-cost data structure, at least, that just backs onto the implicit language behavior, and makes people ask themselves what the special magic thing that's happening is?

I don't follow why C++ interop is important.  C++ doesn't have an ABI, and you have to essentially be a C++ compiler to call half of it because templates.  By the time you degrade to a shared C interface, drop order shouldn't matter.

I am not an industrial coder, but I have been using C++ for years, and this is the first time anyone has even told me that arrays destroy in reverse order, let alone that I should feel free to rely on it in production code.

If this is truly needed, then you can make a ReverseDropArray<N> as soon as rust gets type-level integers, and it will act just like regular arrays in every way.

Regarding internal referencing, it seems like the strong argument from that feature to preserve an undefined drop order & require ManuallyDrop, so that we can reorder the self-referencing fields.

@withoutboats That would be the dropck-validating order, right? I wondee what @pnkfelix thinks.

With a zero-overhead ManuallyDrop<T>, I'd also be fine with order remaining unspecified. The current workaround has runtime overhead which I'd like to avoid.

Well, we already have unions, just waiting for them to get stabilized. It is pretty trivial to implement ManuallyDrop using unions.

@eddyb i would prefer to specify some drop order, and force the programmer to actually write their definition so that the internal references don't violate dropck constraints

If self-ref structs would indeed be required to be declared in a dropck satisfying order, then the reverse-drop case gets stronger.

I personally find it more intuitive for fields to depend on the fields above them in self referential structs, which would imply reverse drop order.

I think it depends on the case. Consider a self-referential HList, in that case it seems common for the head to reference the tail, and the head is more natural to declare first.

@camlorn: Bargaining is the third stage.

@ncm Your posts are becoming increasingly unconstructive and hyperbolic; I'd urge you to stop and reflect.

@ncm
I don't actually have much of an opinion.  Or any real sway.  To me it seems arbitrary, and this is what i am pointing out.
I don't see how I'm bargaining either.  It's a feature of C++ I have never, ever used.  It's a feature of C++ I will be going out of my way to never, ever use.  If this thread didn't exist, I wouldn't even be aware it was a feature in the first place.  In order to be bargaining, I'd have to miss it to start with.

@withoutboats that may be true, but I agree with @retep998 that, overall, a top-to-bottom ordering for self-referential structs seems more intuitive. It's also the most convincing argument I've seen for changing the ordering so far, though I'm still not sure if it's worth the subtle breakage of existing code.

@comex: Well, you know, anger is the second stage, but it's not a race. :wink:

@ncm Applying the Kübler-Ross model to people with legitimate questions and concerns is only going to make you a laughingstock. Either you're severely misguided and have no idea how to engage in constructive discussion, or you're trying to troll us. Either way, it's not helpful.

One thing I'm curious about is that, under the premise that existing code relying on this unspecified behavior is sufficient grounds to prevent change, would taking advantage of any theoretical advantages of the order being unspecified to optimize also be considered a breaking change and therefore barred? Presumably the drop order was unspecified in the first place so that such optimization could take place, but if it can't, then what does "unspecified" really mean in terms of observable semantics?

@jpernst From a "legalistic" perspective we're within our rights to make a change here, given that we left the order explicitly unspecified. But from a practical perspective, changing the order has real costs for people using Rust today, and risks setting back the perception that Rust is stabilizing. So, while it's not out of the question to change it, we'd need more than "theoretical advantages" to justify doing so.

Moderator note
@ncm, please stop. Your comments are hyperbolic, belittling, insulting, and in general not constructive, and you have been warned multiple times. This is not acceptable for discussions on this forum. You may wish to go through our code of conduct
I suggest you take a step back to cool down, after which you should feel free to participate constructively here. Further such nonconstructive comments will lead to a permanent ban from all official Rust venues like this repo.
Everyone else: I recommend you don't engage with the hyperbole, however much you may want to 😄

@withoutboats To be honest, I am not overly concerned about the possibility of fields with references into the struct. No such structs exist today -- at least not without using unsafe pointers -- and if they ever did come to exist, then the type system would have to know about it. In that case, we could adjust the drop order to account for such dependencies. It seems likely to me that to support such structures elegantly would require some sort of special lifetime that plays a role more analogous to an associated lifetime of the struct, in any case, and that declaring such a lifetime is going to take some syntax. That syntax may indeed inform the drop order. I suppose that may be something we come to regret, but in the worse case we can provide some sort of attribute to allow user's to explicitly control the drop order (and hence structs with internal references, which are, after all, a rarity, may opt for the "reverse" order).

The Draft International Standard for ISO 14882 C++ Language 2017 was voted out Saturday (the final committee step before it becomes an IS, replacing C++14) so the pressure is off in Kona.  I apologize for the upset in this forum. The carnival atmosphere seemed to call for levity, but what felt like general mirth really came only the people physically around me.  (Without dwelling overmuch, I can say that all of dozens of representatives I spoke with were at least astonished at the course of this discussion.)
As recompense, I will take time to try to answer the questions asked, carefully and sincerely. I would be less happy, and the world poorer, if Rust were to lose viability as an alternative to C++ in applications where no other plausible alternative is even on the horizon.  I hope the astute reader will forgive repetition of essential distinctions that certain readers have missed, repeatedly, before, and for re-stating what may seem obvious.  I aim to leave no one behind.
I will begin by introducing an example, as concrete as I can make it here, of the flavor of applications dependent on LIFO destruction/drop order. This example involves arrays because, ultimately, it is in arrays and array-like objects that the need for default natural-order drops is most acute.  By report, Rust already provides easy control over the order of destruction of struct members, so I see no need to discuss member drop order in this example.  (Readers may recall that the programs that were cited as depending on the de-facto drop order were unaffected by array drop order.)
The example involves an array in which each element is a struct with a member that identifies a physical device that interacts with the program, and with other devices, and with the world beyond. Dropping an element/struct initiates read/write operations on one or more device file descriptors, according to other state in the object and on results of the operations. (In this example, none of the objects' struct members has Drop; just the element/struct itself.)
The dependency relationships between the devices identified are unavailable to the program.  All that can be known is that a device identified in one array element object may depend on other devices identified by previously-constructed array elements.  In consequence, the operations initiated in a Drop of element j must precede those for all elements i < j, and follow those for all i > j.  This implicitly ensures that all the (unknown but temporally bounded) actual dependence relationships are respected.
One might observe, as @camlorn did, that even a C program could respect the relationships manually by stepping through the elements in the right order.  While the observation is valid as far as it goes, it would be equally valid about any use of drops. Our languages support drops for reasons: programs that rely on drops have proven to be inherently more trustworthy and maintainable.  Giving that up is not an appealing prospect.  To have to give it up to use Rust would stop many trials of Rust before they began.
One might observe, as @camlorn did, that a custom container could be composed and documented specifically to drop in the required order.  Equally as valid, it has several problems.  First, is a custom container really more trustworthy than the formal language specification?  A custom container may be changed, where well-specified core language features do not. A custom container may have bugs, where core language features are thoroughly tested in a myriad of configurations.  High-reliability code minimizes unnecessary dependencies, and favors the strongest guarantees, otherwise.
Furthermore, on both accounts, modern, large software systems are necessarily constructed by composition from components.  We get maintainable, understandable systems by restricting the interaction between separate components. A consequence of this separation is that one component must often act on whatever another component provides, with no place to plug in its own, custom apparatus.  To use a custom container would take extra mechanism to communicate this need and act on it, which would, at best, reduce the maintainability and reliability of the system, and might be prohibitively disruptive. Systems are simpler and more reliable when optimal ground rules are universal, and fundamental types suffice for interaction among subsystems.
@camlorn asks whether C++ interoperability matters. That depends on how quickly users of Rust would like it to penetrate domains dominated today by C++.  At present, Rust and C++ structs are layout-compatible for the subset of common types, as are their array layouts and, potentially, many library types including optional, tuple, and array view, to name a few. The implementations, for some common subset, of lambdas and destructors/drops in LLVM could, in principle, be shared between Clang and Rustc. Most new code is patched into existing systems. Most mature Rust code will be in subsystems for decades to come, because older code goes away very slowly.  (tl;dr: Rust's choice is interop or niche.  Thus far the project has chosen interop.)
It is always tempting to ask an experienced engineer operating under difficult constraints, "Can't you just ... ?" The answer is almost always, (1) "We thought of that", often followed by (2) "we're not morons." (Occasionally, it's "Yes, but not yet.")   But shouldn't it sometimes be Rust's problem?

The carnival atmosphere seemed to call for levity, but what felt like general mirth really came only from the people physically around me.

It was made abundantly clear to you that people were not happy with your unconstructive comments. Don't act like it wasn't, just be more careful in future interactions.

Drop of element j must precede those for all elements i < j, and follow those for all i > j. This implicitly ensures that all the (unknown but temporally bounded) actual dependence relationships are respected.

You've specifically designed a program here that depends on a certain drop order, and used it as an argument that Rust should follow that drop order.
You are repeatedly saying that this is the more natural drop order, without any justification aside from this vague notion of "interop". The same reasons it makes sense in C++ do not necessarily carry over. This was made clear to you at the very beginning of this thread (where it was also abundantly clear that you do not know the details of Rust's design), and yet you have continued.
Once again you're talking about construction order; Rust has none -- you cannot partially construct structs and arrays and have the construction of "newer" elements depend on older ones. Rust structs get constructed in the order you specify the fields at construction time.  If you're in such a specific situation where you have interdependence of resources, it's more than fair that that may incur an additional (minor) bit of complexity.
Rust does not have default implicit construction, so every time you construct it you get to (and must) choose the order of construction. Given that, none of this is particularly relevant.

The implementations, for some common subset, of lambdas and destructors/drops in LLVM could, in principle, be shared between Clang and Rustc.

Drops work differently in Rust anyway, I don't think this is possible. Rust has drop flags and stuff (which are dealt with explicitly in C++ types via move constructors), so having Rust own C++ types is tricky anyway due to more unavoidable reasons. In general for interop you want to avoid owning C++ types since that brings in a whole deal of other things like move constructors and stuff. This is stuff you have to think about anyway. I've been writing a lot of FFI with C++ for more than half a year now, and while we do often have cross-FFI ownership, we are careful and conservative about it for a bunch of other reasons; this doesn't really make that situation worse.

So, there is a question I've been wondering. Let's suppose for a second that we were sufficiently convinced that we ought perhaps to change the default drop order on vec -- could we do it? How would we do it? I can imagine e.g. a warning period on any Vec whose element types define Drop, but that seems... like it will flag a lot of things it oughtn't (e.g., Vec<Vec<_>>), though perhaps with enough white-listing we can narrow the list considerably (but only for libstd types). We can use cargobomb to run tests, at least...
(There seems to be general agreement that we would, technically, be within our rights to do so.)
I do feel that reverse drop order is perhaps slightly less likely to lead to bugs, although I'm really not sure, it depends ultimately on what people expect. (I think we can all agree that whatever order we adopt, it would be easy to control how any given instance behaves; the question is are you likely to implicitly make assumptions without realizing it.) It has been argued that forward drop order is more efficient (although that sounds dubious to me and also of secondary concern).

@Manishearth : I'm afraid I cannot respond to your posting, because I cannot see any way anyone could reach your conclusions from what I wrote.  Did you miss that @camlorn and @Ixrec asked me questions that seemed to deserve complete answers?
@nikomatsakis :

I suggested a compiler command-line flag that would keep forward-drop-dependent programs working until they could be fixed, something like --forward-array-drop.
To start out assuming that order doesn't matter, and then conclude the same, does not suggest deepening understanding. You don't need to trust me. There are many thousands of people, with decades of experience each, who would be happy to try to explain.

Applications where order doesn't matter are easy to find; it's clear your experience is there. Places where order is crucial are equally easy to find. It takes nothing away from your experience to admit others' different experience. You give talks introducing Rust in many different places. Say, "The Rust project is thinking about standardizing on destroying array elements in index-order, from low to high.  Raise your hand if that strikes you as mad." Surprise yourself.

Did you miss that @camlorn and @Ixrec asked me questions that seemed to deserve complete answers?

I don't see how that is relevant? I never said they didn't. I was just addressing the technical points in your post.

Unless you are talking about this post, in which case, if you can't see how I reached that conclusion, please step back, because that conclusion is one reached by many many people who have asked the moderation team to deal with this. If you can't see how you were being hyperbolic, you are not doing anyone any favors by continuing the conversation, since you're likely to do it again.
(If you would like to reply to this non-rfc-related discussion, please just directly email rust-mods at rust-lang.org)

I was just addressing the technical points in your post.

To do that would have required actually, you know, reading it. Doesn't moderating imply an ethical obligation of some kind?

Please take further discussion of moderation to the moderation list. This is rapidly veering off topic.

Right now, the drop order is still unspecified, so one could submit issues or pull requests to use ManuallyDrop to any crate that depends on it, no?  And doing so helps make them more explicit anyways.

Here's a summary of the arguments for reverse drop order. Please tell me if I missed any.
Pros

Consistent with other languages like C++, leading to less surprise and more intuitive behavior.
Consistent with the order that local variables are dropped.
Effectively means that each field in a struct has a dependency on the field defined above it, which feels more intuitive. Note that arrays can be initialized in any order.
Effectively means that each entry in an array has a dependency on the entry before it, which feels more intuitive. Especially notable because arrays only have one practical order to initialize them.
Does not prevent anyone from having a custom drop order using ManuallyDrop.

Cons

Could potentially break some code that relies on the current behavior, as unspecified as it is. Some of these cases are easily fixable by using ManuallyDrop but actually finding all these cases could be difficult.
Using reverse drop order would mean agreeing with someone you might not like to agree with.

Let me quickly give my take on this.
I gave it a :-1: as I believe we are better off with an unspecified order, as it

gives more freedom to the compiler for optimization (doesn't involve UB, but it can then reorder drops in whatever way it wants). Reordering can allow merging some drops.

tells the programmer not to rely on any particular order (which — I think — is widely agreed upon to be unidiomatic).

The first argument is weak without an example, so let's consider something like
let rc = Rc::new(0);

let rc1 = rc.clone();
let im = SomeTypeWithADestructor(rc.clone());
let rc2 = rc.clone();

// Drop order: rc1, im, rc2

Unless we can somehow prove independence of im (which could be impossible for all kinds of reasons), there is no way we can merge rc1 and rc2.
If we could reorder, we could make the order rc1, rc2, im, which can be reduced to rc1+rc2, im by doing count -= 2; instead.
This isn't terribly advanced optimization, and I imagine it is within the scope of MIR.
Especially the case of reference counting is interesting. The decrementing, incrementing can be quite expensive, but merging them like this can improve performance a lot.
Without reordering it is much harder (and often impossible) to do.
Now, let's go to 2.:
Telling the programmer that the drop order is guaranteed could incite relying upon the order, which I think we can agree is really bad practice. It isn't a complete argument, but it is certainly a concern worth keeping in mind.

@burdges ManuallyDrop RFC #1860 has only recently moved to FCP, and isn't yet merged. It also depends on union, which is still unstable (so an external library providing it isn't quite possible for stable users).
Agreed though, that once we have it in stable, moving in that direction (instead of relying on drop order for safety) is a good idea.

Making (or relying on) drop order of arrays and other array-like datatypes seems to be reverse seems to potentially be more foot-gunny to me (compared to unspecified or forward).
Consider this code (as I’ve argued on IRC) in context of reverse drop order (can also be adapted similarly for arrays):
let v = vec![/* of some elements that have inter-dependencies */];
for index in ..(v.len()) {
    // do something to v[index]
}

Now, somebody comes along and rewrites the code to this, more idiomatic version:
let v = vec![/* of some elements that have inter-dependencies */];
for elm in v.into_iter() { // or `for elm in v`
    // do something to elm
}

It is not obviously incorrect, but the first version will drop elements in whatever order we happen to specify and the 2nd version will drop the elements as the vector (or array, once it gains a IntoIterator impl) is iterated, in a forward order. Similar issue can be described for Drain iterator, although it is only applicable to Vecs.

That being said, I do not think we should associate the decision much with storage of inter-dependent resources, because one can think of many ways to write safe idiomatic Rust code where such order can be disturbed (within structs and arrays alike).

@ticki Out of curiosity, do you think Rust should have left the order of evaluation of argument expressions to calls unspecified?

@retep998 would you mind revising your bullet points to indicate which points are specifically pro (or con) with respect to reverse drop order specifically, versus which points are solely about mandating any specific drop order, be it forward-order or reverse-order?

(For example "Relying on drop order is unidiomatic" is a con against any specification of drop order, right?)

@retep998 (also, " would mean agreeing with someone you might not like to agree with" both a straw-man. One could plug in any choice that has some opponent into that sentence. Also I found it a bit passive aggressive...)

@jmesmon

It's implimentation also depends on union, which is still unstable (so an external library providing ManuallyDrop isn't usable for stable users, unless there is an impl without using union).

The union is an implementation detail and standard library can use unstable implementation details.
Other libraries cannot define unions on stable, but they can use ManuallyDrop even if it's a union.

@petrochenkov right, my point there was we can't have something on crates.io and bypass waiting on stable ManuallyDrop. We need at least stable union.

Nominating for discussion at lang team meeting, specifically to attempt to address my concern labelled internal-references-future-proofing
Some positions of note on this concern:

@withoutboats wrote https://github.com/rust-lang/rfcs/pull/1857#issuecomment-283468883
@pnkfelix later responded with his preferred (family of) solutions https://github.com/rust-lang/rfcs/pull/1857#issuecomment-283481922

(I am somewhat tempted to concede to the @nikomatsakis position and resolve my concern. I will stew on this further in the hours remaining before the lang team meeting; maybe I will remove nomination flag...)

@pnkfelix please keep it nominated; given the ongoing discussion I think it'd be good for the lang team to touch base.

@rfcbot resolved internal-references-future-proofing
(as noted above, I was convinced by @nikomatsakis argument that if we do add support for internal references, we can and probably will also add some simple way to control the drop order for such cases to ensure that dropck constraints are satisfied.)

@rfcbot resolved internal-references-future-proofing

The lang team spent some time discussing this RFC in our last meeting. A couple of points emerged:

Given that we're already quite wary of making a change here, it seems like stabilizing on some order in the near future is warranted; a change to the order is only going to get harder, and it doesn't seem like we're going to get drastic new evidence over time.

It would be helpful if a proponent of reverse-order drop could make a branch of the compiler where Vec drops in that order. We could then run it through cargobomb, which is capable of running test suites, to get a lower bound on the fallout.

While it seems almost everyone would prefer dropping in reverse order if breakage were not an issue, there's still not terribly clear evidence that forward order will cause problems in practice. As has been argued previously on thread, it seems most likely to be a problem for those coming from C++, but there are many differences, some of them subtle, between the two languages, and it's not clear that this one poses special danger.

To try to put it sharply: if we were to change drop order now, we'd be risking silent breakage of production code. Suppose that causes a severe problem for a production user; what answer would we give? A legalistic stance of "you shouldn't've been relying on it since it was unspecified, and we're changing it to something more intuitive" is not incredibly compelling. Does anyone have a new argument to make on this front?

I think we can also optimize the example Rc case in some cases without being concerned about the order.  It should be possible to mark some types in the standard library safe for reordering by implementing an unsafe trait, in this case something like impl<T> ReorderedDrop for Rc<T> where T: !Drop.
Just an idle thought.

@aturon wrote: "It would be helpful if a proponent of reverse-order drop could make a branch of the compiler where Vec drops in that order"
I have mixed feelings about reverse drop order, but I decided to have a go at this task anyway.
Changing Vec (and arrays/slices in general) requires only a small patch.
Branch is here: https://github.com/pnkfelix/rust/tree/reorder-vec-drop
I'll get in touch with brian about trying to run that through a cargobomb.

@pnkfelix did you ever give that a spin?

@nikomatsakis no I have not. I'll email brian.

sorry for the radio silence here. the basic answer is that cargobomb is not designed to be applied to arbitrary branches of rust, so brian was not able to throw my experiment through cargobomb.
not quite sure what best next step is if we are serious about trying to actually evaluate the experiment. crater alone does not seem like a sufficient foundation for learning about the impact of changing drop order.

Anymore, I don't feel like it's necessary to change drop order. I just want it stabilized. I'd rather it were backwards, because I think it's less confusing, but either way, I just want to be able to rely on some sort of drop order.

All right, I think this has sat long enough. Let's reach a decision. Ultimately, I still don't see much value in changing things. I propose we merge the RFC as is.
@rfcbot fcp merge

Seems most people expect dropping in reverse initialization order, isn't that the default behavior one wants for reasoning about RAII/Transactions? Letting existing buggy code decide the direction the language doesn't make sense as the decision it will affect all future code.

"More Rust code will be written than has been written"

I'm as much of a proponent of reverse order as anyone, but I'm OK with this. Rust structs have a stronger semantic guarantee of no internal dependencies (stuff like rental notwithstanding) and so the field drop order shouldn't actually matter in general. For unsafe libs, special care must be taken anyway. Further, changing the order now would mean that, forever into the future, there will be old versions of the rust compiler in existence that won't conform to the specified behavior, and will miscompile programs that rely on it. Anyone writing code that relies on reverse drop, who may never have even used rust before this decision was made, would need to be aware that there are versions of the compiler for which the behavior of their code will be silently different. For these reasons I'm willing to accept forward drop, even if it's not ideal. We'll still have things like ManuallyDrop if we want to change it.

@jpernst yeah, I'm kind of okay with it. It just makes me sad.

I'd assume v.drain(..).map(..).collect() would alway drop v: Vec<T> in forward order, no?

Further, changing the order now would mean that, forever into the future, there will be old versions of the rust compiler in existence that won't conform to the specified behavior, and will miscompile programs that rely on it.

That makes sense, in that case I would prefer to see it postponed until a major version change. Not sure what the plan is for Rust 2.0 but stabilized drop order + relevance/linear types would be great to have https://github.com/rust-lang/rfcs/issues/814

@spearman If we're going to go the route of postponing drop order stabilization until a certain major milestone like Rust 2.0, then we should ensure the drop order cannot be relied on until then, which means we need to randomize drop order in the mean time.

@retep998 That doesn't really make sense.  The entire reason to wait is to avoid the consequences of breaking code that relies on forward order.

(sorry for taking so long to check off my box; I've really been dropping the ball on my RFC reviews... :( )

After having read this entire thread, here's my viewpoint, and it's largely the same as what I expressed 3 years ago.
I understand why a lot of people think vectors should drop in reverse order. It's really handy if you're using the vector like a stack. But I still think that it doesn't actually make intuitive sense for vectors, arrays, tuples, or structs to drop in reverse order, because they aren't stacks. Local variables are a stack, not just technically but also conceptually. Variables declared first have a longer lifetime, maybe just technically if declared in the same scope, but especially if declared in a parent scope.
But structs, tuples, arrays, and vectors have no intrinsic stack nature to them. Structs and tuples are simply values, and arrays and vectors are ordered representations of values, where most processing on these ordered collections happens in-order. Having values get dropped in reverse-order would be pretty surprising to me, especially if you consider nagisa's comment where they point out that, if Drops happen in reverse order, then inserting a single .into_iter() into an iteration on a vector would flip the Drop order and this would likely be surprising to the reader. And similarly, structs and tuples, while not having an intrinsic ordered nature to them, are still not stack-like. Especially tuples, because a tuple is basically just a heterogenous array, and should behave similarly (e.g. a (T, T, T) and a [T; 3] should Drop in the same order).
And finally, I think there's a lot of value in standardizing on the current implemented behavior today, because that means we won't break any code, and has been pointed out repeatedly, if we were to flip the order, then there's not really a great way to fix existing code (that relies on Drop order) such that it's still compatible with older compilers.
So given all that, I'm strongly in favor of this RFC. Except there's one tweak that needs to be made: The RFC currently says "slices and vectors" match drop order of structs. Except slices don't have a drop order, and the RFC as written doesn't mention arrays. I assume that section was meant to say that "arrays and vectors" should match the drop order of structs.

Also, regarding the whole notion of intra-struct references, if we do extend the language to let you express those, then I think it's perfectly reasonable to also adjust the Drop order to take those dependencies into account, rather than forcing the writer to reorder their fields.
In addition, I think there's value in adding support later on for attributes like #[drop_order(reverse)] or #[drop_order(memory)]. The latter so that way people who do end up with huge structs can squeeze the extra performance out of dropping, and the former mostly just because if we support the latter then people are going to wonder why we don't support the former.

I feel I should also point out that while I agree in principle that leaving drop order undefined would let us do nice things down the road, in practice it's a bad idea because, even without defining it, people will (and already have) write code that depends on the current behavior. Yes, that code is technically "broken", but that doesn't mean we can ignore the issue, because it's perfectly reasonable for people to write code like this without even realizing that they're depending on unspecified behavior.

@kballard

in practice it's a bad idea because, even without defining it, people will (and already have) write code that depends on the current behavior.

If we agree that the current code is not big enough to warrant setting this for all future Rust code, we could just randomize the drop order of a struct during compile time. That way it won't be "perfectly reasonable" for people to write code like that.

Is there some example (real-world) code that relies on drop order?

@tbu- Randomizing the drop order would also introduce random code failures though:

You would only be able to check if it works by running the code
Some builds will work and some won't

I agree that code that relies on drop order should be explicit about it, but I think randomizing drop order will only create sour experiences for those that do rely on it (given that they may not know or remember that they are relying on it in a specific place). If we could statically verify that the user relies on drop order and give them an error, then I would be fine with leaving it undefined, but that doesn't seem plausible.

If you're already running the code to check, then you apparantly already know about drop order.

Some builds will work and some won't

Well, this is better than implicitly relying on the drop order if it is unspecified, right?

I'm not saying you run your software to test for drop order specifically; I'm just saying you won't ever know the software is wrong if you don't run the specific code under the right conditions, and because of the randomized drop order, those conditions will be slightly different every time you build.

Well, this is better than implicitly relying on the drop order if it is unspecified, right?

Of course, if nothing is done to stop people from implicitly relying on it, then it may as well become defined, since changing it will then cause too much breakage. But I'm not sure randomizing the drop order is the right answer, since it feels like something that could introduce very subtle errors. Best case is that the random ordering causes a panic immediately, but since drop shouldn't generally fail, it might as well just cause an off-by-1 error somewhere completely different, or worse. So to me it feels like we're trading one kind of badness for another this way.

Just while everyone is thinking about it : Is there enough need for dropping in reverse order to be worth giving a standard way to do it?
pub struct ReverseDropVec<T> { _vec: Vec<T> }
impl<T> Deref for ReverseDropVec<T> {
    type Target = Vec<T>;
    fn deref(&self) -> &Vec<T> { &self._vec }
}
impl<T> DerefMut for ReverseDropVec<T> {
    fn deref_mut(&self) -> &mut Vec<T> { &mut self._vec }
}
impl<T> Drop for ReverseDropVec<T> {
    fn drop(&mut self) {
        while let Some(e) = self._vec.pop() { } // ::std::mem::drop(e);
    }
}

I'd expect the answer to be no.  And merely adding Deref, DerefMut, and others for wrappers
to the derive_more crate cuts the boilerplate by at least half here.

@tbu- Real-world code that relies on current drop order has already been linked upthread. And I completely agree with @KasMA1990, randomizing drop order is a bad idea, because it means you can test your code locally and everything will work, but then it will randomly fail for users, simply because when you tested it the compiler picked a compatible drop order and when your users used it the compiler picked a different drop order.

Huzzah! At long last, this RFC has been merged. Thanks, @aochagavia, and the many others who participated in this discussion.

Given that this is just a matter of policy rather than implementation, was this RFC effectively serving the role that stabilization tracking issues on the rust repo usually do, i.e. this thing is now considered stable and there are no further steps in the process?

@glaebhoerl Oops, I neglected to link the tracking issue.
I'm not totally sure what work is needed, but at the very least there are some documents that should be changed to reflect that we're now specifying a drop order.

@aturon - WRT,

I'm not totally sure what work is needed, but at the very least there are some documents that should be changed to reflect that we're now specifying a drop order.

At the very least, shouldn't there be unit tests for the compiler to ensure regressions don't occur with respect to drop order? If it is going to be guaranteed to follow th order of today (forward), shouldn't there be tests to confirm that this doesn't inadvertently change in some future version of the compiler.

@gbutler69 Indeed! I'll add that to the tracking issue.