I'm not sure whether something like this has been suggested before but I'm thinking something along the lines of:
pub trait ShiftMove {
/// moves the item at index `from` to the index `to`, shifting
/// all items in between in the appropriate direction.
fn shift_move(&mut self, from: usize, to: usize);
}
impl<T> ShiftMove for Vec<T> {
fn shift_move(&mut self, from: usize, to: usize) {
if from == to {
return;
}
assert!(from < self.len());
assert!(to < self.len());
unsafe {
let from_ptr = self.as_mut_ptr().add(from);
let to_ptr = self.as_mut_ptr().add(to);
// SAFETY: from < len
let elem = std::ptr::read(from_ptr);
if from > to {
// shift all elements between `to` and `from` to the right by one index
std::ptr::copy(to_ptr, to_ptr.add(1), from - to);
} else {
// shift all elements between `from` and `to` to the left by one index
std::ptr::copy(from_ptr.add(1), from_ptr, to - from);
}
// SAFETY: to < len
std::ptr::write(to_ptr, elem);
}
}
}
#[cfg(test)]
mod test {
use crate::ShiftMove;
#[test]
fn test1() {
let mut vec = vec![1, 2, 3, 4, 5, 6];
vec.shift_move(3, 0);
assert_eq!(&vec, &[4, 1, 2, 3, 5, 6]);
}
#[test]
fn test2() {
let mut vec = vec![1, 2, 3, 4, 5, 6];
vec.shift_move(0, 3);
assert_eq!(&vec, &[2, 3, 4, 1, 5, 6]);
}
}
The implementation is inspired by Vec::remove
and Vec::insert
.
Comparison to Vec::remove
+ Vec::insert
: Compiler Explorer
Ideally this'd be implemented for slices, however Miri complains about stacked borrows if I just change the impl type.