Hmm, this seems more plausible than most similar discussions since it’s an optimization over moving, not a requirement that something other than memcpy be done. (So a Vec<ArrayVec<>> could still just realloc its buffer, for example.)
Strawman with terrible idents:
unsafe trait OnlyNeedToMoveUpTo {
fn only_need_to_move_up_to(&self) -> *const ();
}
default unsafe impl<T> OnlyNeedToMoveUpTo for T {
fn only_need_to_move_up_to(&self) -> *const () {
unsafe { (self as *const Self).offset(1) as *const () }
}
}
unsafe impl<T> OnlyNeedToMoveUpTo for ArrayVec<T> {
fn only_need_to_move_up_to(&self) -> *const () {
// assuming len is before the storage
unsafe { self.as_ptr().add(self.len()) as _ }
}
}
Then the compiler could emit the memcpy for that length instead of the type’s full size, and in the normal case it would still be a const, so the same codegen would happen.
(Other options and variations include returning ranges instead of just prefixes, using lengths instead of pointers, deciding this is a terrible idea, making it not take self so people can’t import this on everything, having it required to be a const fn, and a whole bunch of other things I’m sure I’ve forgotten.)