[Withdrawn] `~` as the early drop point marker? (Addressing problems of RFC PR 210, Take 2)


#1

[This proposal is also withdrawn.]

When discussing RFC PR 210 today, I realized that InferredLt/ScopedLt don’t solve any real problems of QuietDrop/NoisyDrop. So I now withdraw that proposal.

I said to @arcto in the RFC’s comments, “Other than lints, an editor powered by the compiler’s future analysis API may be able to highlight implicit dropping points.”

And then I realized, “using an editor” is not the only way to highlight something.

As @pnkfelix finds out, doing explicit early dropping is painful. But, we don’t need to be too explicit.

As @glaebhoerl points out, the actual dropping rule may still be (relatively) simple, just different.

And, though (typically) not as good as compilers, programmers are still good at reasoning about codes, just a little visual help may make a huge difference.

Design:

Reintroduce the ~ sigil as the early drop marker.

It can be used to mark a block, like: { ... ~}.

Which means “there is at least one early drop happening in this code block, but not in its nested blocks”.

And { ... } now means “there is no early drop here”.

Rules:

  1. When the compiler try to insert an early drop, it checks the enclosing block of the (to be) drop point, if the block is not marked by ~, a compile error happens, otherwise, the compiler inserts the drop.
  2. On the other hand, if the compiler finds { ... ~}, but this marked block doesn’t immediately contain (to be) early drop points, a compile error also happens.

Advantages:

  1. Blocks immediately containing early drop points are guaranteed to be marked, and there are no false positives, the code will be easier to reason about even without any tools. Code with early drops will never look innocent, that is to say, if a piece of code looks innocent, it is.
  2. Explicitly writing drop calls are not required.
  3. { ... ~} is a lightweight notation, changing between { ... } and { ... ~} is very easy.
  4. C++ uses ~ for destructors, and Rust can use it to mean “(early) calls of destructors”.
  5. The effect of ~ doesn’t apply to nested blocks, so people will not use it like Java’s throws Exception.
  6. This can be used in conjunction with QuietDrop/NoisyDrop. (Though our experiments with static drop may prove that, with the reintroduction of ~, QuietDrop/NoisyDrop are unnecessary. If so I’ll be very happy. :slight_smile:

Drawbacks:

Some keyboard layouts don’t have ~.

When we removed ~ from Rust, this was one of the minor reasons, and the fact that ~ is lightweight, is an advantage now. Time changes.

Unresolved Questions:

What about match arms? Do we write => do_something(...) ~,? Or => { do_something(...) ~}?


#2

I think that most of the time early drops are totally uninteresting. In some special cases (for example locks) you do care about the drop location. This is where you would use the NoisyDrop trait.

Because in most cases side effects of drops are uninteresting, I don’t think there’s a need to mark the scopes where these are used.


#3

It depends on use cases. For example, unexpected early drops that take a long time, that free much memory, that should have happened at a later specific time for some reason, may or may not be a problem, depending on what the code is for. Tagging specific types to be NoisyDrop is insufficient, as everyone has different requirements.

Personally I don’t code for use cases where the above would be problems, so I don’t care about implicit early drops too much. I can even give eager drops a try. :stuck_out_tongue:

But there are people who would care.

~ would make future Rust code easier to reason about, and No one will be surprised, Rust newcomers or not, as { ... ~} doesn’t exist in any programming language that I know.


#4

I’d like to hear a specific use case for this feature – note that you must explicitely move the ownership out of the function for this to happen at all, so that alone is very rare.


#5

I’d be surprised if 1 in 10000 early drops caused unexpected/undesired results. Because of this, it is so much more convenient to just have them implicit by default. Every little piece of unnecessary complexity makes a language just a little bit less pleasant to use.


#6

Actually I realized when preparing the RFC for ~, it may actually be easier to reason about dropping behaviors with static drops than with dynamic drops.

The idea is that we do not see “early drops” as “early drops”, but “special moves” inserted by the compiler to satisfy one guarantee - “all branches of a given branching operation must move the same set of objects”.

So, at any point in a given program, the liveness of any object can be statically determined. That’s not possible with dynamic drops.

And I know believe the ~ sigil is unnecessary.


#7

I realized the term we use, early drop, is a sign of the real problem. Compared to C++, yes we seem to be going to do early drops. But that’s because C++ cannot do move semantics completely right, due to legacies. And we have the oppotunity to do it right now.

I’ll post my take 3 later. :slight_smile: