DerefMove without &move -- why don't we have it?

DerefMove has long been one of the holes in Rust, that many have proposed to implement, but no one has quite figured out. The blocking issue seems to be the lack of &move (aka &own) references.

But it just occurred to me that DerefMove can totally be implemented without &move references, using the following signature:

trait DerefMove: DerefMut {
    fn deref_move(self) -> Self::Target;

Yes, this would have to be desugared slightly differently than Deref[Mut]:

&*x -> &*Deref::deref(&x)
&mut *x -> &mut *DerefMut::deref_mut(&mut x)
*x -> DerefMove::deref_move(x)
//   ^^--- no dereference here

...but I don't see how this would be a problem from either the compiler's or users' perspective.

Also, it wouldn't be able to support moving back the way you can with a Box, but that's a special case (and if I understand correctly, that wouldn't be supported by the proposed &move implementation either).

Are there any issues with this implementation? Why hasn't Rust taken this approach?

(To be clear, this isn't a proposal; I'm just trying to understand why I haven't seen this implementation anywhere)

Question. What would let y = &*x; do, for a T that is both Deref and DerefMove?

&* is already merged into a single operation that has magical ability to cancel out the dereference.

What would be the benefits of this reduced version? AFAIK the main reasons we wanted one were &move references and making Box less special, and neither are addressed by this one. It also doesn't really allow writing code with different semantics than the ones we already have with a .into_inner() method/function.

1 Like

It would Deref.

It's actually already ambiguous with a DerefMut type: &*x could call DerefMut and create a shared reference out of that, but it doesn't because it recognises that for a shared reference calling Deref is enough.

So this kind of DerefMove wouldn't be an outlier in this sense.

1 Like


Well, I don't know.

I think it does make Box less special (any DerefMove type could be moved out of via dereference), but not not special at all: it wouldn't support moving back in.

Another thing it would allow is calling self-eating methods of the Target type directly.

Oh interesting... I always thought that having DerefMove was a motivation for having &move references and not the other way around.

So was DerefMove not implemented just because without &move references there wouldn't be enough motivation for it? (and not because it couldn't be?)

Box would still have to continue using the old special implementation because you can't build its features with magic on top of DerefMove. That is, a pseudo-implementation like this would not work:

impl<T: ?Sized> DerefMove for Box<T> {
    fn deref_move(self) -> T {
        // Avoid dropping `self` since that would also drop the value inside, which we will move out.
        let this = ManuallyDrop::new(self);
        let value = unsafe { core::ptr::read(this.ptr) };
        unsafe { alloc::alloc::dealloc(this.ptr) }

Box still needs to support moving back in, while this implementation needs to free the allocation, so you can't have both at the same time. Thus you need to keep the current Box implementation and just implement deref_move as *self. IMO this means that Box is still as special as before, you just gave user defined types a bit of syntax sugar to make them look like a Box, but with a different implementation.

And if we ever want to actually make Box not special, how would this trait fit in? If Box can't use it I would expect it to be replaced with something else, but then why add it in the first place?

&move references would allow passing ownership or values around without actually moving them, which is useful by itself. They are also a pretty flexible way to implement DerefMove, but not the only one.

AFAIK it was never implemented because we never reached a consensus on its design.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.