How can I swap two elements in a 2d vector?


#1

Hello again,

Yet another beginner question, this time about swapping elements in a 2d vector.

Given this 2d-vector

let mut m = vec![
    vec![1,2,3],
    vec![4,5,6],
    vec![7,8,9]
];

I can swap the first and last elements by doing

let tmp = m[0][0];
m[0][0] = m[2][2];
m[2][2] = tmp;

However, this does not seem to work in this example, where grid is a struct field defined as grid : Vec<Vec<Tile>>

fn swap(&mut self, src_r : usize, src_c : usize, dst_r : usize, dst_c : usize) -> ()
{
    let t = self.grid[src_r][src_c];
    self.grid[src_r][src_c] = self.grid[dst_r][dst_c];
    self.grid[dst_r][dst_c] = t;
}

How can I solve this? Is there some mem::swap function I should use?

Thanks in advance :slight_smile:


#2

First, if Tile is a small object, consider making it a Copy type (#[derive(Copy)]), so it will be easy to work with. Instead of “can’t move out of borrowed context” error, the compiler will copy it.

Swap can be used, but there’s a catch. The borrow checker doesn’t understand that vec[0] and vec[1] are different! So when you borrow both, it just sees vec borrowed twice and complains. The solution to this is

let (left, right) = vec.split_at_mut(position);

split_at_mut gives you two separate slices pointing into two non-overlapping areas in the same vector or slice.

BTW: there’s no 2D vector in Rust. What you have is a “jagged array” where every row is allowed to have a different dimension. If your grid has a fixed size, then 2D array may work ([[Tile; width]; height]). There’s imgref crate that gets close to being a 2D vector by adding 2D addressing to a 1D vectors. It’s designed for images, but would work with other types.


#3

@kornel Thanks! Deriving copy and clone worked.

I will attempt the mem::swap as well, will let you know how it goes :slight_smile:


#4

If you don’t want to clone Tile but it has a default value, you can also use two mem::replace calls:

fn swap(&mut self, src_r : usize, src_c : usize, dst_r : usize, dst_c : usize) -> () {
    let t = ::std::mem::replace(&mut self.grid[src_r][src_c], Tile::default());
    self.grid[src_r][src_c] = ::std::mem::replace(&mut self.grid[dst_r][dst_c], t);
}