Adolfo Ochagavía

Thoughts on changing Rust's drop order

There seems to be a wide consensus that the current (unspecified) drop order in Rust is weird and arbitrary. If you are not yet convinced, you should definitely take a look at a previous post exploring the current rules.

While changing the drop order seems attractive, there are many aspects that need to be considered before embarking in such a quest. In this post I would like to expand on the consequences of changing the drop order.

An ideal scenario

Let’s start with the assumption that nobody is relying at this moment on the current drop order. That is, all library authors abide to Rust’s best practices and there are no crates on crates.io that rely on this implementation-specific detail. I highly doubt this is the case, but we can assume it just for the sake of the argument (note that a crater run would be a possible way to test whether this assumption holds).

Suppose you are writing a library which internally uses unsafe, but exposes a safe interface. When figuring out how to expose said interface, you realize that you need to drop the fields in one of your structs in a specific order. Otherwise you are treading into the realms of Undefined Behavior.

Fortunately, there has just been an RFC that changes Rust’s current drop order to something sane. It is even stable! Of course, you decide to use it. Much better than relying on the unergonomic workarounds we have been using up to this moment.

Breakage

The scenario described before seems to be ideal. However, the library would silently break if compiled with an old enough compiler. That is, in case you are using a compiler that knows nothing about the new drop order, the library will trigger undefined behavior.

The issue here is that the drop order depends on the version of the compiler, but there is nothing in the code that gives a hint about which order should be used. Furthermore, as a library author, you don’t know which compiler will be used by your clients, which means that you cannot trust on a given drop order.

A partial solution

Any solution requires coming up with a way to ensure old compilers reject the new code. For instance, we could require a crate attribute like #![stable_drop_order]. This way, an old compiler would fail with the following error:

The attribute `stable_drop_order` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642)

Problem solved? No way…

Back to the beginning

Even with the new crate attribute in place, there is still an unanswered question: what happens if a programmer forgets to add the necessary attribute? Which drop order would be used then? As you can see, we get the same undesirable situation as today: the default drop order is still unspecified and unstable.

In fact, you end up with two different drop orders. An official one, which can be activated on a per-crate basis by using a special attribute; and an unspecified one, which “should not be used” but is still there.

This situation would probably lead to a de facto stabilization of the unspecified drop order. Other implementations of Rust would be forced to copy it to avoid accidentally breaking code.

The conclusion is clear: even if we introduce a sane drop order, the current drop order needs to be stabilized anyway. It a pity, but I think there is no way around it. I would really love to be proven wrong.

A possible compromise

While I think that stabilizing the current drop order is the way to go, there is still an alternative that could be considered. Namely, if a library did not opt in to the new drop order (by using the #![stable_drop_order] attribute), the new compiler could assume that the author meant to use the new drop order anyway.

This way, we solve the problem described in the previous section, since there is no longer an unspecified drop order. Furthermore, forgetting to add the attribute would be considered a bug in the library and should be fixed by the author.

However, there are two big counter arguments for this compromise:

The latter problem could be treated as a library bug as well. However, the fact that the bug would only be reproducible in some versions of the compiler makes it particularly pernicious. You would need to be very picky about picking your dependencies after updating to a recent compiler.

Conclusion

As you probably noticed, I am in favor of stabilizing the current drop order. Once this happens, we could start thinking about introducing an alternative opt-in drop order. However, the concrete details are subject of a different discussion.