1

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

bge0
  • 901
  • 2
  • 10
  • 25
  • Please create an [MCVE](/help/mcve) that compiles on the [Rust Playground](https://play.rust-lang.org/). For example, this [works just fine](http://is.gd/0GYnam). – Shepmaster Sep 10 '15 at 19:50
  • hmm that works because you are only adding to one element.. I want to set a range as mentioned in the heading. http://is.gd/ULkN6j – bge0 Sep 10 '15 at 19:53
  • Possible duplicate of http://stackoverflow.com/questions/28219231/how-to-idiomatically-copy-a-slice or http://stackoverflow.com/questions/25225346/how-do-you-copy-between-arrays-of-different-sizes-in-rust – Shepmaster Sep 10 '15 at 20:19
  • Unfortunately not. Both of these methods append to the beginning of the array. I found something in the link from the first one, i.e. http://doc.rust-lang.org/std/primitive.slice.html#method.move_from however it is unstable with a comment on whether it should be in the API. Is there a better way? Also, why the downvote? – bge0 Sep 10 '15 at 20:58
  • *Both of these methods append to the beginning of the array* — partially true (look into what "array", "Vec" and "slice" all mean in Rust). However, you can always create a new slice that starts later. That's what `chunks_mut` does. – Shepmaster Sep 10 '15 at 21:11

3 Answers3

1

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);
      }
    }
bge0
  • 901
  • 2
  • 10
  • 25
0

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.

Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • No I understand copy & swap..... Albeit the example is contrived. I would be storing the chunk I collected and then replacing it with something else as opposed to how the example is written, so it would not be duplicated elements. Can I not collect a chunk_mut to replace it in entirety? Is there no way to move data internally without an entire copy? Seems a little excessive to double memory consumption for a move op. – bge0 Sep 10 '15 at 21:20
0

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.

Kaplan
  • 2,572
  • 13
  • 14