Iterating over a vec and moving values to a new one


#1

I am trying to do something that should be easy but I’m failing at it :stuck_out_tongue:

I have a struct like the following one:

pub struct EntityList {
    entities: Vec<Entity>,
    to_add: Vec<Entity>,
    to_awake: Vec<Entity>,
    to_remove: Vec<Entity>,
}

and a function that should iterate over the to_add vec and move those elements into the entities vec actually changing the ownership of the elements.

    pub fn update_lists(&mut self) {
        if self.to_add.len() > 0 {
            let mut i = 0;
            for to_add_it in &mut self.to_add {
                let entities_contains = self.entities.iter().any(|e| *e == *to_add_it);
                if !entities_contains {
                    let elem = self.to_add.remove(i);
                    self.entities.push(elem);
                    to_add_it.entity_added();
                    to_add_it.awake();
                }
                i += 1;
            }
            self.to_add.clear();
        }
}

but it’s failing because I’m first borrowing it through the iterator and then trying to borrow it again with the call to remove.

The actual error is the following:

error[E0499]: cannot borrow `self.to_add` as mutable more than once at a time
  --> src/entitylist.rs:49:32
   |
46 |             for to_add_it in &mut self.to_add {
   |                                   ----------- first mutable borrow occurs here
...
49 |                     let elem = self.to_add.remove(i);
   |                                ^^^^^^^^^^^ second mutable borrow occurs here
...
56 |             }
   |             - first borrow ends here

There must be a better way to accomplish this. Any suggestion is welcome. Thanks in advance!


#2

You can use a plain indexed for loop:

// for to_add_it in &mut self.to_add {
for x in 0 .. self.to_add.len() {
   // get the to_add_it using the index
...
}

Or you can use drain:

for to_add_it in self.to_add.drain(..) {
    let entities_contains = self.entities.iter().any(|e| *e == *to_add_it);
    if !entities_contains {
        // call entity_added() and awake() while you still own to_add_it - after pushing to entities, it moves in there                
         to_add_it.entity_added();
         to_add_it.awake();
         self.entities.push(to_add_it);
     }
    // no need to clear() the vec - the Drain takes all elements out.
}

#3

Thanks @vitalyd. Drain seems like the best choice. I tried to work it out with the simple indexed loop but I always end up borrowing the element so that I cannot move it out of the original vector and push it in the new one.


#4

I think drain fits your case quite well. You can also drain manually with something like:

while let Some(e) = self.to_add.pop() {
   if self.entities.iter().any(|x| *x == *e) {
          e.entity_added();
          e.awake();
          self.entities.push(e);
   }
}

Note that pop removes from the tail of the vector, and so I’m not sure if you need to preserve order of elements as you push them to entities.

One thing I didn’t mention in my previous reply is it’s easy to panic the index-based code due to out of bounds access since it’s indexing the vec and removing items from it.


#5

Yes, drain worked very well for my use-case. Now I’ve got some more problems with the borrow checker but I’ll open a new thread for that one as it’s not directly related to this issue.
Thanks so much!