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>
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
.
§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§
Sourcefn deref_move<'frame>(
self,
storage: DroppingSlot<'frame, Self::Storage>,
) -> MoveRef<'frame, Self::Target>where
Self: 'frame,
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.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.
Implementations on Foreign Types§
Source§impl<P> DerefMove for Pin<P>
Note that DerefMove
cannot be used to move out of a Pin<P>
when P::Target: !Unpin
.
impl<P> DerefMove for Pin<P>
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;
}