0

I have a vector of vectors, and the number of vectors can change at runtime. I therefore have:


struct ItemStruct {
    old: usize,
    transfer: usize,
}

//...

let number_of_vectors = x.len();
let mut ListOfItems = vec![vec![0; 0]; number_of_vectors];

there is then some code to add to the internal vectors, which all goes fine.

However, later on I need to move items between vectors (inside one vector) and I always trip up from the ownership rules:

 for item in anotherList.iter() {
        let to_move = ListOfItems[item.old].rchunks(4).next().unwrap();
        ListOfItems[item.transfer].append(&mut to_move.to_owned())
    }
error[E0502]: cannot borrow `ListOfItems` as mutable because it is also borrowed as immutable
  --> src\main.rs:53:9
   |
52 |         let to_move = ListOfItems[item.old].rchunks(4).next().unwrap();
   |                       ----------- immutable borrow occurs here
53 |         ListOfItems[item.transfer].append(&mut to_move.to_owned())
   |         ^^^^^^^^^^^ mutable borrow occurs here ----------------- immutable borrow later used here

How do I move an item from one vector to another? If I new the number of Vectors at compile time I would do it differently, but because its variable, I have gone with creating a vector of vectors. I know I could do this in C and in Python but I also understand why Rust doesn't like it!

birdistheword99
  • 179
  • 1
  • 15
  • Do you want to remove the last 4 elements from the `old` vec and move them to the `transfer` vec, or do you just want to copy them and still leave the ones in old? – PitaJ Dec 07 '22 at 17:25
  • @pitaJ Move them, so one vector gains the 4 elements and the other loses them – birdistheword99 Dec 07 '22 at 17:28

1 Answers1

2

The way to modify two portions of a single slice of memory is using split_at_mut

// Always split between the two points
let (old, transfer): (&mut Vec<_>, &mut Vec<_>) = if item.old < item.transfer {
    let (left, right) = ListOfItems.split_at_mut(item.transfer);
    (&mut left[item.old], &mut right[0])
} else {
    let (left, right) = ListOfItems.split_at_mut(item.old);
    (&mut right[0], &mut left[item.transfer])
};

// rchunks isn't the right way to move the last 4 elements
// `to_owned` will copy them
// instead, we use `drain` and `extend`
let to_move = old.drain(old.len()-4..);
transfer.extend(to_move);
PitaJ
  • 12,969
  • 6
  • 36
  • 55
  • 1
    At first this didn't seem like the right answer, but I now understand what you're doing. You are splitting the main vector and getting references to each side of it, then performing the actions. Smart! Is there much overhead to the `split_at_mut`? Is it creating a copies of each partition of the vector or just getting a reference to a slice of it? – birdistheword99 Dec 08 '22 at 09:10
  • 2
    There is practically zero overhead - it's just creating references to each portion. And it's very likely that part will be optimized out. There is no copying involved. – PitaJ Dec 08 '22 at 14:49