Pre-RFC: Move references

Drop flags and initialization state are equivalent. Drop flags are the (potentially runtime, potentially compile time) tracker for whether a place needs to be dropped, i.e. whether it is in an initialized state.

If the drop flag is "on," the place it is tracking is initialized and needs to be dropped. If the drop flag is "off," the place it is tracking is not initialized (either pre-initialization or post-drop, it doesn't matter) and needs to be not dropped or otherwise referenced (safely).

Drop flags typically are discussed w.r.t. being emitted at runtime, but the exact same system is used for tracking pre-init and post-drop state of places, even when they're known statically.

This doesn't even need to matter! &move out is returnable. Proof:

fn proof<'a, T>(r: &'a move out T) -> &'a move out T {
    r
}

It is more consistent to have move references follow the normal borrowck rules than to introduce new rules to forbid something like this.

So... you mean the normal borrowck rules for locals? There's no need to add a concept for lifetime of the current stack frame, because we already have it based on the way lifetimes work. Just take a reference to a stack local, and you have a reference which is most loosely constrained (before you add additional constraints) to begin no earlier than when the stack value was initialized, and end no later than when the stack value is dropped.

If you're assuming general typestate changing references (see later in this post): you may be right! But for the basic case of &move out, the standard lifetime rules handle it perfectly fine.

No they don't. As written, &move inout even does not expose PIT in any way. At the point you obtain &move inout, the pointee is fully constituted. At the point you give away &move inout (incl. by end of scope), the pointee is fully constituted. At the point you obtain &move out, the pointee is fully constituted. At the point you exit scope with &move out, the pointee is fully dropped. There is no dynamicism, no runtime sharing of partially dropped places, and everything on function borders is known to be either fully initialized or fully dropped.

However, @tema2: I maintain that &mut inout T is semantically equivalent to &mut T w.r.t. ownership and responsibilities as observed by the outside, and that you haven't done anything to solve the unwinding problem by separating them. I still think that in and inout both, as well as DerefMove, need to be deferred to a later RFC step, but if you want to keep &move inout as part of the RFC, please show in a concrete way how it behaves differently than &mut, and show how I can move out of a &move inout and then panic safely.)

I actually do see where @Soni got the impression of the need for &mut DropFlags, though: a sound &move inout in the face of panics does require communicating back to the caller which fields have been moved from in the face of a panic.

Still, @Soni: please, rather than asserting that something is true, show why it must be true. You've obviously convinced yourself, so convince others by showing them why what you're saying is true, not just asserting that it is. If someone currently disagrees with you, telling them (multiple times) what your position is isn't going to change anyone's mind. Showing evidence can.


Also, @Soni, IIUC your PIT proposal doesn't handle the case that &move inout is trying to address; that is, typestate does nothing to allow fn(&mut T(..)->T(..)) (using your syntax) to move from the reference temporarily, as a panic would then leave the place in a more uninitialized state. (And if you want the "magic word" to communicate what you're trying to do with your PIT proposal, I believe it is in fact typestate.)

But his is still not the thread to discuss PIT, except perhaps in how &move needs to behave to remain forward compatible with an initialization typestate future. That thread is ?Uninit types [exist today]. Also let's talk about DerefMove.

5 Likes