14

I am new to Rust. I need to create a vector before a for loop. Run for loop on it. Change the vector inside the for loop. Then Change the vector after the for loop.

I tried the following code and tried to use immutable borrow but both did not work.

fn main() {
    let mut vec1 = vec![4, 5];
    vec1.push(6);
    for i in vec1 {
        if i % 2 == 0 {
            vec1.push(7);
        }
    }
    vec1.push(8);
    println!("vec1={:?}", vec1);
}

I expect to compile and change the vector inside and after the for loop. But it shows this error message:

error[E0382]: borrow of moved value: `vec1`
 --> src/main.rs:6:13
  |
2 |     let mut vec1 = vec![4, 5];
  |         -------- move occurs because `vec1` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
3 |     vec1.push(6);
4 |     for i in vec1 {
  |              ----
  |              |
  |              value moved here
  |              help: consider borrowing to avoid moving into the for loop: `&vec1`
5 |         if i % 2 == 0 {
6 |             vec1.push(7);
  |             ^^^^ value borrowed here after move

Can you explain why move occurs? Can you make it compile?

Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
  • 1
    You can't do this. Pushing a new item into a vector could cause it to reallocate. If you are only ever adding items to the end of the vector, then you can solve this by looping over vector indices instead of elements, so that the vector isn't borrowed by the loop. – Peter Hall Aug 24 '19 at 11:18

2 Answers2

24

There are two problems with your code. Luckily, both of them can be solved by changing one thing. The problems are:

  • Writing for _ in vec1 moves the value vec1 into the loop. This is a move like every other move, meaning that the value is not accessible after the loop! If you need a refresher on ownership and moving, please read the related chapter in the Rust book. You can iterating over references to a vector's elements via for _ in &vec1.
  • You are trying to mutate the vector you are iterating over. Regardless of whether you iterate over the vector by value (as you do) or by reference, you cannot change the vector while iterating over it. And there are many good reasons for that! In your case, you could easily end up having an infinite loop if you add elements while iterating.

To solve both problems at the same time, you can iterate over indices to the vector instead of the vectors elements (Playground):

let mut vec1 = vec![4, 5];
vec1.push(6);
for i in 0..vec1.len() {
    if vec1[i] % 2 == 0 {
        vec1.push(7);
    }
}
vec1.push(8);
println!("vec1={:?}", vec1);

This way, the vector is not moved nor borrowed by the for loop and we are free to mutate it during and after the loop. This particular solution iterates over the indices of the original vector, meaning that elements added in the loop are not iterated over by the loop. This is a good protection to avoid accidental infinite loops. Please note, however, that you can still shoot yourself in the foot by, for example, removing elements from the vector during iteration. In general, independent of programming language, mutating collections while iterating over them is dangerous and should only be done with care.

Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
  • "In your case, you could easily end up having an infinite loop if you add elements while iterating." That's not the reason why it's forbidden though. 1) Infinite loop are perfectly safe. 2) If he were to *remove* elements, that couldn't cause an infinite loop, and it's still forbidden. The reason is it might cause elements to relocate, and you'd end-up with dangling references. – mcarton Aug 24 '19 at 12:55
  • @mcarton I just mentioned two easy to understand examples, but never claimed this is the underlying reason for how Rust works. Also, this dangling-references problem depends on how the iterator is implemented. It could be implemented in a completely safe manner, that would never result in memory unsafety. The actual reason why this is not allowed is because *Rust says so*. – Lukas Kalbertodt Aug 24 '19 at 13:09
0

you can do it with loop like this, but be careful.(Playground)

fn main() {
    let mut vec1 = vec![4, 5];
    vec1.push(6);
    
    let mut i = 0;
    loop {
        if i >= vec1.len() { break; }
        if vec1[i] % 2 == 0 {
            vec1.push(7);
        }
        
        i += 1;
    }
    vec1.push(8);
    println!("vec1={:?}", vec1);
}
likev
  • 1
  • 1