bge0
bge0

Reputation: 921

How to set a range in a Vec or slice?

My end goal is to shuffle the rows of a matrix (for which I am using nalgebra).

To address this I need to set a mutable range (slice) of an array. Supposing I have an array as such (let's say it's a 3x3 matrix):

let mut scores = [7, 8, 9, 10, 11, 12, 13, 14, 15];

I have extracted a row like this:

let r = &scores[..].chunks(3).collect::<Vec<_>>()[1];

Now, for the knuth shuffle I need to swap this with another row. What I need to do is:

scores.chunks_mut(3)[0] = r;

however this fails as such:

cannot index a value of type `core::slice::ChunksMut<'_, _>`

Example: http://is.gd/ULkN6j

Upvotes: 1

Views: 5764

Answers (3)

Kaplan
Kaplan

Reputation: 3728

that's probably closer to what You wanted to do:

fn swap_row<T: Clone>(matrix: &mut [T], row_src: usize, row_dest: usize, cols: usize) {
    let v = matrix[..].to_vec();
    let mut chunks = v.chunks(cols).collect::<Vec<&[T]>>();
    chunks.swap(row_src, row_dest);
    matrix.clone_from_slice(chunks.into_iter().fold((&[]).to_vec(), |c1, c2| [c1, c2.to_vec()].concat()).as_slice());
}

I would prefer:

fn swap_row<T: Clone>(matrix: &[T], row_src: usize, row_dest: usize, cols: usize) -> Vec<T> {
    let mut chunks = matrix[..].chunks(cols).collect::<Vec<&[T]>>();
    chunks.swap(row_src, row_dest);
    chunks.iter().fold((&[]).to_vec(), |c1, c2| [c1, c2.to_vec()].concat())
}

btw: nalgebra provides unsafe fn as_slice_unchecked(&self) -> &[T] for all kinds of Storage and RawStorage. Shuffeling this slice avoids the need for row swapping.

Upvotes: 0

bge0
bge0

Reputation: 921

I ended up doing a loop over and an element by element swap which seems like a cleaner implementation to me:

    fn swap_row<T>(matrix: &mut [T], row_src: usize, row_dest: usize, cols: usize){
      for c in 0..cols {
        matrix.swap(cols * row_src + c, cols * row_dest + c);
      }
    }

Upvotes: 1

Shepmaster
Shepmaster

Reputation: 431031

Your code, as you'd like to write it, can never work. You have an array that you are trying to read from and write to at the same time. This will cause you to have duplicated data:

[1, 2, 3, 4]
// Copy last two to first two
[3, 4, 3, 4]
// Copy first two to last two
[3, 4, 3, 4]

Rust will prevent you from having mutable and immutable references to the same thing for this very reason.

cannot index a value of type core::slice::ChunksMut<'_, _>

chunks_mut returns an iterator. The only thing that an iterator is guaranteed to do is return "the next thing". You cannot index it, it is not all available in contiguous memory.

To move things around, you are going to need somewhere temporary to store the data. One way is to copy the array:

let scores = [7, 8, 9, 10, 11, 12, 13, 14, 15];
let mut new_scores = scores;

for (old, new) in scores[0..3].iter().zip(new_scores[6..9].iter_mut()) {
    *new = *old;
}

for (old, new) in scores[3..6].iter().zip(new_scores[0..3].iter_mut()) {
    *new = *old;
}

for (old, new) in scores[6..9].iter().zip(new_scores[3..6].iter_mut()) {
    *new = *old;
}

Then it's a matter of following one of these existing questions to copy from one to the other.

Upvotes: 0

Related Questions