Is it possible in Rust to add some hook that is called on object unique address:
let obj = MyStruct { ... };
...
get_struct(obj); // Get struct by value that mean that address of this object on stack could be changed
impl AddrHook for MyStruct {
fn address(&self, addr: *const Self) {
// Do some handling or save somewhere
}
}
I guess, but this has been discussed before, and the conclusion that not having custom moves is far better than any benefits that custom moves can provide. This because it makes unsafe code far easier to check. Just imagine if let y = x; could panic!
Additionally, we would have to modify every existing collection to handle these custom moves, i.e. Vec<_> would have to call the custom moves on resize. This has some far reaching effects that would make it very hard to implement.
let x: T = ...;
let y = x;
// will be translate to something like
let y: T;
unsafe { OnMove::on_move(&mut x, &mut y); }
Even if you just need the address, it has all the same concerns as full blown custom moves because it would need to be applied in all the same places. So what happens if AddrHook panics or otherwise? Would it be safe to not call AddrHook when something moves? If no, then this means we can no longer blindly copy things around like we do in say Vec<_> on reallocation.
There may be another way around this, so this sounds like an X-Y problem.
That's not the point. You still have to call some custom behavior on every move.
let mut v = Vec::with_capacity(1);
v.push(MyStruct { ... });
// this second push reallocates, do we call `on_move` for the first element of the vec?
v.push(MyStruct { ... });
If yes, then is means we would have to rework all collections.
(We would have to change all existing collections, not just those in std). This means, ArrayVec, SmallVec, SmallBox, hashbrown, etc. This would almost certainly result in an ecosystem split. That's how fundamental this is.
But what if we just introduce the feature inline trait bound:
impl Vec<T> {
pub fn push(&mut self, value: T) {
// This will panic or abort if we would allocate > isize::MAX bytes
// or if the length increment would overflow for zero-sized types.
if self.len == self.buf.capacity() {
self.reserve(1);
if <T: OnMove> {
// Call on_move traits
}
}
unsafe {
let end = self.as_mut_ptr().add(self.len);
ptr::write(end, value);
self.len += 1;
}
}
}
It would be helpful even for other kind of programming ...
Just because it is possible to implement doesn't mean it's a good idea to implement. Right now, almost all unsafe code assumes that let y = x;has no side-effects, and relies upon this for safety, so adding this feature would break a large majority of unsafe code just by existing.
This makes it nigh impossible to check the safety of unsafe code because we need to check for these invisible moves.
Current semantics of = (as well as passing objects to functions, struct literals, etc.) clearly define that no type will ever have any custom move logic. This is a hard guarantee given by Rust 1.x. Changing that would be a major backwards-compatibility break, and Rust isn't planning such breaking changes.
It couldn't even work with an opt-in for some types only, because such types wouldn't be allowed to touch any code that uses =, function calls or struct literals. They would be useless.
The closest you can do is use Pin for types behind a pointer (where Rust's moves are equivalent to copying the pointer around), and provide a custom method for moving to another Pin.
If the type was behind a pointer, then you could have something like that. BTW: it can't be using &, because borrows are for preventing moves.
let x = Pin::new(MaybeNewAddr::new(x));
let z: MaybeUninit::uninit();
x.move_and_notify(z.as_mut());
let z: Pin<MaybeNewAddr<_>> = z.assume_init();
There were some discussions about having umovable types in Rust. AFAIK currently there's no viable design for it, and Pin is a kinda a library-only partial substitute for this.
If Rust ever gets umovable types and placement new or &out pointers, then maybe it could have more syntax sugar for equivalent of the MaybeUninit<Pin<MaybeNewAddr<T>>>> dance.