DerefMove without `&move` refs

Playground

/// Trait for allowing moving out of custom types
trait DerefMove: DerefMut where Self::Target: Sized {
    /// destroys container; doesn't run `drop` of contained value; called instead of Drop::drop
    unsafe fn drop_empty(&mut self);
    
    /// dereference to a place with a value. Actual moves happen with that place
    fn deref_place(&mut self) -> Place<'_,Self::Target>;
    
    /// should be called each time after a place got from `deref_place()` gets out of scope:
    /// * this includes pointer style move-ins;
    unsafe fn notify(&mut self) {}
}

Pro:

  • We go for a more complex desugaring => we can completely avoid the need for &move references;
  • The desugaring for more complex cases can be altered, so it's guaranteed to not touch stack.

Con:

  • Cannot handle true DST moves: functions that take them as arguments (dyn FnOnce()) would need to receive them as a pointers => indirection.

Notes:

  • The motivation behind DerefMove::notify is to allow more complex scenarios than a box;
  • Dropping: when DerefMove::deref_place gets called it becomes responibility of the caller to dispose a value from gotten place, this includes maintaining drop flags for that place. When holder value gets empty because of move out it doesn't go through plain drop path - it instead goes through DerefMove::drop_empty.

So you're basically proposing to change compiler-tracked destructive moves into a "move leaves an unspecified state", like in C++? I think I'll pass.

DerefMove in particular is nowhere near important enough to introduce such hacks into the language.

No, I propose to allow destructive moves on arbitrary types by allowing them to return pointers to their held values, so they can be moved in\out later. A value or it's part is considered to be moved out from the call to deref_place until return from notify;

They are called together on moving in a value or its part.

This way place gottten from value that implements DerefMove is considered to be partially initialized when it's content gets moved out, as with Box:

let mut x = Box::new(S {a: 10,b: "a".to_string()});
let b = x.b; // here, x.b is moved out (wow); here `DerefMove::deref_place` has been called
//here x is destroyed through `drop_empty`, right after calling `Drop::drop_in_place(DerefMove::deref_place().ptr as &mut _)`

This way, move leaves specified state.

Do you have an example of a situation that would require notify?

Is this any better than the prior art of MoveNew? MoveNew in moveit::new - Rust

1 Like

Example use case for this I imagine is DOMNodeHandle that deref's to DOMElement type; Or building some kind of reactive containers (that know whether they have got anything changed in them). Interacting with handles to buffers not in main RAM (so that notify triggers transfers); Not sure if such an implicitness is justified though.

This is simpler, I don't want to go for any &move references because they can only give us move out functionality (given no linear types).