For move construction (T(T&&)
), I believe this is typically true. (Obviously, it is possible to subvert this.) However, this is distinctly not true for move assignment (T& T::operator=(T&&)
), as it's somewhat common practice to implement it as a swap.
Although, the first rule about using the rvalue reference as scratch space is (that it will be dropped. But we care about the second rule, which is) that you don't know when the value will be dropped. General advice is that if it's okay to be dropped "eventually," you can leave owned state in it, but if you want more deterministic destruction, drop eagerly, and leave the rvalue reference in a null object state. (Obviously, it is possible to strengthen the guarantees of move assignment for a specific type to specify the state of a moved-from value.)
Because the type itself defines what it means for that type to be pinned (e.g. Unpin
opts out of all pin-added guarantees), I think a model wherein a PinMove
trait opts out of the "same address until destruction" in a very controlled way (i.e. you can use the "move constructor" to copy-move it to a new location). I think Pin
would have to agree to this, though, under strict language lawyering. (Especially since Pin
impacts the memory model's rules.)
However, we can work around this (for C++ types) rather trivially: just do the C++ thing, and leave a valid value there. Then you follow Pin
's rules exactly: the value is still there, and you drop it before destroying the storage. You just did a weird copy-and-mutate earlier.
This would work transparently for C++ types, as it would just use the C++ semantics for move and then destruct. It wouldn't work for Rust types, though, because (most) don't have an equivalent "valid but unspecified" null object state that would skip doing work in the destructor.
I think there is a way to define PinMove
such that it follows the law, however, though it may be kind of tricky to do so. The TL;DR is that you say that it copies the value to the new location, potentially mutating the existing value for efficiency, knowing that it then "drops" the existing value, satisfying Pin
's requirements. And since the type itself defines what that operation is, I think that we can skip actually calling Drop::drop
and "drop" the old value inline.