Swap parts of two vectors

Hey folks,

What would be the most efficient way of swapping halves of two vectors of owned values?
That is we have two vectors of the same type, I'd like to replace the second half of the vector a with the second half of the vector b, and, correspondingly, the second half of the a (the original one) with the second half of the b:

before:
a = [1,2,3,4,5,6]
b = [10, 20, 30, 40, 50, 60]

after:
a = [1,2,3, 40, 50, 60]
b = [10, 20, 30, 4, 5, 6]

Because these are non-overlapping, I was hoping to make this in-place, without cloning (the real case uses large stucts, not numbers).

All I could come up with was something like

let a_mid = a.len() / 2;
let b_mid = b.len() / 2;

let r = a.splice(a_mid.., &b[b_mid..]);
b.splice(b_mid.., r);

but it doesn't even compile :frowning:

Just swap them one-by-one, usually the compiler optimize them to the most efficient machine code.

fn slice_swap<T>(left: &mut [T], right: &mut [T]) {
    assert_eq!(left.len(), right.len());

    for (l, r) in left.iter_mut().zip(right) {
        std::mem::swap(l, r);
    }
}

let half = a.len() / 2;
slice_swap(&mut a[half..], &mut b[half..]);

On the godbolt I've used std::process::abort() instead of the assert_eq!() to skip generating debug-printing code which is unnecessary on this showcase. You can see both cases are efficiently vectorized but the actual code vary due to the properties of the actual type.

1 Like

If the two vectors are the same length, use the swap_with_slice method:

a[a_mid..].swap_with_slice(&mut b[b_mid..]);

If not, you could use swap_with_slice to move as much as the shorter vector will allow, and then v1.extend(v2.drain(...)) to move the remaining items:

fn swap_back_halves<T>(a: &mut Vec<T>, b: &mut Vec<T>) {
    // Use `a` for the longer vector:
    if b.len() > a.len() {
        std::mem::swap(a, b);
    }
    
    let a_mid = a.len() / 2;
    let b_mid = b.len() / 2;
    
    let b_back = &mut b[b_mid..];
    let a_back = &mut a[a_mid..][..b_back.len()];

    // Swap the back half of `b` into `a`.
    a_back.swap_with_slice(b_back);
    
    // Move any remaining elements of `a`.
    let a_len = a_mid + a_back.len();
    b.extend(a.drain(a_len..));
}
6 Likes

thanks, folks!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.