Hello,
I have a loan problem.
In the update function, I browse the vector '"Alien"
And I must also create lasers. Hence, compilation error second mutable borrow occurs here
There is probably a design problem in my way of doing things. But I do not see how to fix it.
pub struct Game {
/* other data */
pub hero: Hero,
pub lasers: Vec<Laser>,
pub aliens: Vec<Alien>,
}
impl Game{
/* other code */
pub fn update(&mut self) {
/* other code */
for (_pos, alien) in self.aliens.iter_mut().enumerate() {
if alien.chrono_shoot >= 60.0 {
self.create_laser(
alien.sprite.x
+ (alien.sprite.width as f32 * alien.sprite.scale) / 2.0,
alien.sprite.y,
0.0,
3.0
);
}
alien.update();
}
}
fn create_laser(&mut self, x: f32, y: f32, vx : f32, vy : f32) {
let mut laser = Laser::new();
laser.sprite.x = x - laser.sprite.width as f32 / 2.0;
laser.sprite.y = y - laser.sprite.height as f32 / 2.0;
laser.vx = vx;
laser.vy = vy;
self.lasers.push(laser);
}
/* other code */
}
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/game/mod.rs:199:17
|
12 | for (_pos, alien) in self.aliens.iter_mut().enumerate() {
| ----------------------------------
| |
| first mutable borrow occurs here
| first borrow later used here
13 | if alien.chrono_shoot >= 60.0 {
14 | self.create_laser(
| ^^^^ second mutable borrow occurs here
The problem is that &mut self argument gives exclusive access to all of self, including self.aliens, so technically self.create_lasercould call something like self.aliens.clear() and derail the outer loop.
The borrow checker checks interfaces, not implementation. This is deliberate, because it allows libraries to modify internals of functions without risk of breaking anyone, but it also means that the borrow checker doesn't care that you're not actually using self.aliens in self.create_laser. You could, and that's not allowed.
There are two immediate ways to work around it:
Create helper functions (static methods) that only get references to the fields they need, not all of self
Use Mutex or RefCell to allow shared mutable access to some fields, and then use shared &self instead of exclusive &mut self.
The bigger fix is to use ECS pattern instead of arrays of big mutable objects:
Sorry for the late response, I looked at the possibility of rewrite in ECS rather than pseudo OOP. Even if ECS does not have only advantages (they speak often of slowness, not a lot of tutorial). But it adapts well to the phylosophy of RUST (functional programming in this case)
@Pent, I just tested the code you offered me, this walk in my game.
I looked at this 2nd solution to use ECS pattern.
@leudz, at first reading I made an error of interpretation, as regards the optimization at the processor level, the prediction of the instructions between the model OOP and ECS, or precisely it is very hard for the processor in OOP to predict the instruction unlike ECS or it's big series of loops.
On the other hand for ECS, there is not much information contrary to the OOP in C ++
Typically it is very easy to organize its code in C, C ++ or Rust a file class for struct (with header for C and C ++) but with ECS under rust for the moment I put everything in a single file and it becomes already too long for me.