pub unsafe trait DerefMove: DerefMut + AsMove {
    // Required method
    fn deref_move<'frame>(
        self,
        storage: DroppingSlot<'frame, Self::Storage>
    ) -> MoveRef<'frame, Self::Target>
       where Self: 'frame;
}
Expand description

Moving dereference operations.

Note: This trait is intended to be defined in conjunction with AsMove, and there is a subtle interdependency between the two traits. We recommend also reading it’s documentation for a better understanding of how these traits fit together.

This trait is the &move analogue of Deref, for taking a pointer that is the sole owner its pointee and converting it to a MoveRef. In particular, a pointer type P owns its contents if dropping it would cause its pointee’s destructor to run.

For example:

  • MoveRef<T> implements DerefMove by definition.
  • Box<T> implements DerefMove, since it drops the T in its destructor.
  • [&mut T] does not implement DerefMove, because it is necessarily a borrow of a longer-lived, “truly owning” reference.
  • Rc<T> and Arc<T> do not implement DerefMove, because even though they own their pointees, they are not the sole owners. Dropping a reference-counted pointer need not run the destructor if other pointers are still alive.
  • Pin<P> for P: DerefMove implements DerefMove only when P::Target: Unpin, since DerefMove: DerefMut.

Principle of Operation

Unfortunately, because we don’t yet have language support for &move, we need to break the implementation into two steps:

  • Inhibit the “inner destructor” of the pointee, so that the smart pointer is now managing dumb bytes. This is usually accomplished by converting the pointee type to MaybeUninit<T>.
  • Extract a MoveRef out of the “deinitialized” pointer.

The first part of this consists of converting the pointer into the “partially deinitialized” form, represented by the type AsMove::Storage: it is the pointer as “pure storage”.

This pointer should be placed into the DroppingSlot passed into deref_move, so that it has a fixed lifetime for the duration of the frame that the MoveRef will exist for. The DroppingSlot will also provide a drop flag to use to build the returned MoveRef.

The mutable reference returned by the DroppingSlot should then be converted into a MoveRef. The end result is that the DroppingSlot owns the “outer” part of the pointer, while the MoveRef owns the “inner” part. The 'frame lifetime enforces the correct destruction order of these two parts, since the MoveRef borrows the DroppingSlot.

The moveit!() macro helps by constructing the DroppingSlot for you.

Worked Example: Box<T>

To inhibit the inner destructor of Box<T>, we can use Box<MaybeUninit<T>> as AsMove::Storage. MaybeUninit is preferred over ManuallyDrop, since it helps avoid otherwise scary aliasing problems with Box<&mut T>.

The first step is to “cast” Box<T> into Box<MaybeUninit<T>> via Box::into_raw() and Box::from_raw(). This is then placed into the final storage location using DroppingSlot::put().

This returns a &mut Box<MaybeUninit<T>> and a DropFlag; the former is converted into an &mut T via MaybeUninit::assume_init_mut().

Finally, MoveRef::new_unchecked() is used to combine these into the return value.

The first step is safe because we construct a MoveRef to reinstate the destructor at the end of the function. The second step is safe because we know, a priori, that the Box contains an initialized value. The final step is safe, because we know, a priori, that the Box owns its pointee.

The use of the drop flag in this way makes it so that dropping the resulting MoveRef will leak the storage on the heap, exactly the same way as if we had leaked a Box.

Worked Example: MoveRef<T>

We don’t need to inhibit any destructors: we just need to convert a MoveRef<MoveRef<T>> into a MoveRef<T>, which we can do by using MoveRef::into_inner(). AsMove::Storage can be whatever, so we simply choose [()] for this; the choice is arbitrary.

Safety

Implementing DerefMove correctly requires that the uniqueness requirement of MoveRef is upheld. In particular, the following function must not violate memory safety:

fn move_out_of<P>(p: P) -> P::Target
where
  P: DerefMove,
  P::Target: Sized,
{
  unsafe {
    // Replace `p` with a move reference into it.
    moveit!(let p = &move *p);
     
    // Move out of `p`. From this point on, the `P::Target` destructor must
    // run when, and only when, the function's return value goes out of
    // scope per the usual Rust rules.
    //
    // In particular, the original `p` or any pointer it came from must not
    // run the destructor when they go out of scope, under any circumstance.
    MoveRef::into_inner(p)
  }
}

deref_move() must also be Pin-safe; even though it does not accept a pinned reference, it must take care to not move its contents at any time. In particular, the implementation of AsMove::as_move() must be safe by definition.

Required Methods§

source

fn deref_move<'frame>( self, storage: DroppingSlot<'frame, Self::Storage> ) -> MoveRef<'frame, Self::Target>where Self: 'frame,

Moves out of self, producing a MoveRef that owns its contents.

storage is a location somewhere responsible for rooting the lifetime of *this’s storage. The location is unimportant, so long as it outlives the resulting MoveRef, which is enforced by the type signature.

moveit!() provides a convenient syntax for calling this function.

Implementations on Foreign Types§

source§

impl<P> DerefMove for Pin<P>where P: DerefMove, P::Target: Unpin,

Note that DerefMove cannot be used to move out of a Pin<P> when P::Target: !Unpin.

// Fails to compile because `Box<PhantomPinned>: Deref<Target = PhantomPinned>` and `PhantomPinned: !Unpin`.
let ptr: Pin<Box<PhantomPinned>> = Box::emplace(moveit::new::default::<PhantomPinned>());
moveit!(let mref = &move *ptr);

// Fails to compile because `MoveRef<PhantomPinned>: Deref<Target = PhantomPinned>` and `PhantomPinned: !Unpin`.
moveit! {
  let mref0: Pin<MoveRef<PhantomPinned>> = moveit::new::default::<PhantomPinned>();
  let mref1 = &move *mref0;
}
source§

fn deref_move<'frame>( self, storage: DroppingSlot<'frame, Self::Storage> ) -> MoveRef<'frame, Self::Target>where Self: 'frame,

source§

impl<T> DerefMove for Box<T>

source§

fn deref_move<'frame>( self, storage: DroppingSlot<'frame, Self::Storage> ) -> MoveRef<'frame, Self::Target>where Self: 'frame,

Implementors§

source§

impl<'a, T: ?Sized> DerefMove for MoveRef<'a, T>