Trait moveit::move_ref::DerefMove[][src]

pub unsafe trait DerefMove: DerefMut + Sized {
    type Uninit: Sized;
    fn deinit(self) -> Self::Uninit;
unsafe fn deref_move(this: &mut Self::Uninit) -> MoveRef<'_, Self::Target>; }
Expand description

Moving dereference operations.

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. To

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 is used to root the storage to the stack in such a way that the putative MoveRef can run the destructor without a double-free occurring. The second part needs to be separate, since the MoveRef derives its lifetime from this “rooted” storage.

The correct way to perform a DerefMove operation is thus:

let mut deinit = MyPtr::deinit(p);
let move_ref = unsafe { MyPtr::deref_move(&mut deinit) };

The moveit!()` macro can do this safely in a single operation.

Safety

Implementing DerefMove correctly requires that the uniqueness requirement described above 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)
  }
}

This criterion is key to the implementation of deinit, which will usually transmute the pointer in some manner.

Associated Types

An “uninitialized” version of Self.

This is usually Self but with Target changed to MaybeUninit<Self::Target>.

Required methods

“Deinitializes” self, producing an opaque type that will destroy the storage of *self without calling the pointee destructor.

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

Do not call this function directly; use moveit!() instead.

Safety

This function may only be called on a value obtained from DerefMove::deinit(), and it may only be called once on it. Failure to do so may result in double-frees.

In other words, every call to deref_move() must be matched up to exactly one call to deinit().

Implementations on Foreign Types

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

Safety

This function may only be called on a value obtained from DerefMove::deinit(), and it may only be called once on it. Failure to do so may result in double-frees.

In other words, every call to deref_move() must be matched up to exactly one call to deinit().

Implementors