Pre-RFC: Move references

Couldn't the drop responsibility be temporarily moved with the &move inout? This way the caller only has to track whether the panic happened during the call where it passed the &movein, while the callee will drop any &move inout on panic, and only on panic. This should avoid the need for passing drop flags along with the move reference, however it doesn't prevent the caller from catching panics.

@tema2 regarding the RFC:

We introduce a new pattern kind ref move NAME : this produces NAME of type &move T! . The reason of the ! obligation is that we may not want to left a binding (partially) deinitialized after execution of a pattern-matching construct.

The reason of such design is that we may not want allow pattern matching to partially deinitialize a binding, as it will require way more complex analysis as well as will give pattern matching powers that do not align well with its original purpose (expressing "high level" logic).

However box patterns allow this, although on nightly only.

I also propose design of DerefMove:

trait DerefMove {
 type Output;

 fn deref(&move self!) -> &move Self::Output!;
}

Could you explain why:

  • DerefMove doesn't extend DerefMut
  • The method name is deref (clashes with Deref::deref)
  • It only works for &move T!
self.ptr as &move T! //just cast the pointer to a reference

This seems pretty weird. You can't currently cast between a *mut T and a &mut T, you need to do something like &mut *ptr. Also, is it supposed to be a safe cast? I see no unsafe there.

Could you explain how it allows Box to not be special cased in the compiler? In particular it isn't clear:

  • How would this method be called? e.g. if I move out of two fields and then back in, is it one or two calls to deref?
    • If one, that needs to be documented where and when it is one.
    • If two, I can't see how the second one would be valid since you didn't initialize back the previous field.
  • How would this allow moving out of a Box without moving back in?

The issue with panics is that they may interrupt modification of referred binding thus resulting in inconsistent state. But this is also true for &mut references, so it may cause only logical bugs.

This is different than exclusive references in that move references allow temporarily moving out of fields. You covered how you would avoid double drops, however there's nothing on what should happen if the caller catches the panic and considers the data to still be initialized.

In any scope of the program, move references created as described above must fulfil their obligations, if any. This means that any data structure holding such a reference is required to use the move reference.

How does this relate to Drop? Is initialization in Drop allowed? Is it even possible since it only provides an &mut T? What about moving them into closures?

his in instance means that &move T! , if something was moved from it, must be initialized back in the same scope in all possible branches. Analysis also must take into account diverging expressions: move reference have to be initialized before return , loop {} and loop {..} resolving to uninhabited types. break is included in the list only if a move reference was created inside a loop that a particular break breaks.

Can't we just ignore unreachable assignments like we do for local bindings? Is there a reason it has to be initialized before loop {}? Since it never returns, the uninitialized stated will never be observed and no drop will ever happen.

Due to the fact that moving a value of !Unpin type most likely will corrupt the data, we may not want it to be moved into and from a binding via such a reference.

That's not true. It's perfectly fine to move an !Unpin type if you have an &mut reference, so it must be safe if you have an &move T!, because it can be coerced into an &mut T.

impl<P> DerefMove for Pin<P>
 where P: DerefMove,
{
 type Output = P::Output;

 fn deref(&move self!) -> &move Self::Output! {
   self.pointer
 }
}

This is unsound, &move Self::Output! can be coerced into an &mut Self::Output, so this would be the equivalent of making Pin::get_unchecked_mut a safe function.

Future possibilities

&move T* kind

This kind of move references obligates to move in referenced binding, doesn't require it to be initialized.

Currently, we have no traits to describe mandatory operations on this kind (it's, in fact, a refined type).

Introducing this would require also Leak and !ImplicitDrop auto traits to describe things correctly.

The reason of not introducing this is that we could not fix soundness issues by only turning off GCE.

It's not clear from this section why &move T* would have problems that &move T! doesn't have.