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>
implementsDerefMove
by definition.Box<T>
implementsDerefMove
, since it drops theT
in its destructor.- [
&mut T
] does not implementDerefMove
, because it is necessarily a borrow of a longer-lived, “truly owning” reference. Rc<T>
andArc<T>
do not implementDerefMove
, 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>
forP: DerefMove
implementsDerefMove
only whenP::Target: Unpin
, sinceDerefMove: 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
Required methods
“Deinitializes” self
, producing an opaque type that will destroy the
storage of *self
without calling the pointee destructor.
unsafe fn deref_move(this: &mut Self::Uninit) -> MoveRef<'_, Self::Target>
unsafe fn deref_move(this: &mut Self::Uninit) -> MoveRef<'_, Self::Target>
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()
.