1

Probably not the right question but trying to learn rust and wondering if there's a way to do this without having to copy out first.

old example

Updated example link

I have a Vec<Vec> where the inner vectors are used as stacks. I want to pop N from vector and push to another but in reverse order

let mut stacks: Vec<Vec<i32>>;
....
let end = stacks[from].iter().take(count);
stacks[to].extend(end);  // <-- fails to compile

Now I understand why this can't happen - end uses an immutable borrow and extend needs a mutable one. I know that the 2 stacks are independent but obviously you couldn't take and push to the same stack. So instead I have to "collect" the 2 off the end (e.g. with split_off but that means creating a new Vec and wondering if there's a way to avoid that?

(This is a simplified example to show the compile error. The original is when I was trying to solve Advent of Code 2022 in Rust , Q5b - The push and pops happen in a loop and can be from and to any one of N stacks, but never to and from the same one.

Edited question to reflect this. Link to full code )

MrPurpleStreak
  • 1,477
  • 3
  • 17
  • 28
  • Is what you really want mutable access to distinct parts of the outer `Vec`? – cafce25 Aug 19 '23 at 21:30
  • @cafce25 maybe? The outer vec remains immutable (as it will always have the same N "stacks" vecs inside ) but those inner vecs should be independently mutable? – MrPurpleStreak Aug 20 '23 at 17:01

1 Answers1

4

You can use split_at_mut to make Rust understand that you are reading and writing two different slots of the vector:

fn main() {
    
    let mut stacks = vec![vec![]; 2];
    stacks[0].push(1);
    stacks[0].push(2);
    stacks[1].push(3);
    stacks[1].push(4);
    
    let n = stacks[0].pop().unwrap();
    stacks[1].push(n);
    
    println!("{:?}", stacks);
    let (left, right) = stacks.split_at_mut(1);
    let end = right[0].iter().take(2);
    left[0].extend(end);
    
    println!("{:?}", stacks);
}

It takes an index, and returns two non overlapping slices, that correspond to the original slice cut at that index.

See the playground.

Alternatively, for more complex use cases, you can use the temporary shared mutation pattern explained in this article. In this specific case, though, the other approach is simpler.

use std::cell::Cell;

fn main() {
    
    let mut stacks = vec![vec![]; 2];
    stacks[0].push(1);
    stacks[0].push(2);
    stacks[1].push(3);
    stacks[1].push(4);
    
    let n = stacks[0].pop().unwrap();
    stacks[1].push(n);
    
    println!("{:?}", stacks);
    let slice = Cell::from_mut(&mut stacks[..]).as_slice_of_cells();
    let second = slice[1].take();
    let end = second.iter().take(2);
    let mut first = slice[0].take();
    first.extend(end);
    slice[0].set(first);
    slice[1].set(second);
    
    println!("{:?}", stacks);
}

See the playground.

jthulhu
  • 7,223
  • 2
  • 16
  • 33
  • Thanks - I cant (yet) get my head around how Cell works but that looks like it might help? However I over simplified my example perhaps - the push/pops happen in a loop where the from, to and count can vary (which means the split_at_mut won't work in that case?). From what I've read Cell is also zero-cost but like I say I've not got my head round it yet. – MrPurpleStreak Aug 20 '23 at 16:59
  • @MrPurpleStreak `Cell` are not *quite* zero cost, in the sense that, in themself, they are zero cost, but they prohibit certain operations (to ensure safety) so you're forced to use (slightly) more expensive options (which are likely to be optimized away). In this case, `.take()` moves the value out of the cell *by replacing it with `Vec::new()`* (which is very cheap, it doesn't even allocate, and is likely to be optimized away). Anyways, `Cell`s are a more advanced feature: if it's too hard for you right now, it's completely legit to do some `.clone()` to keep the compiler happy. – jthulhu Aug 20 '23 at 20:37
  • If you want more examples on how to use `Cell` for accessing in a mutable way different slots of a vector at once, read the article I've linked in my answer. It shows some patterns related to that, and also explains some theory on how to understand `Cell`s in Rust mutable / immutable framework. – jthulhu Aug 20 '23 at 20:47