I recently started self-learning Rust. I went through the Rust book and watched several explanation videos. I am not a professional programmer, but a curious physicist about Rust. As a learning project, I am doing a simple particles simulator, similar to something I did some years ago in C++. I created a struct Particle
, which represents a particle, and a struct Interaction
which represents an interaction between two particles (i.e. a force). Up to here I think things are working. Now I am creating a new structure which is ParticlesSystem
which will hold all the particles and the interactions, and plan to add methods such as calculate_forces
and advance_time
to perform the simulation, and probably some utilities to save the status to a file, etc. This is my complete code:
use euclid::Vector3D;
/// Defines units of position.
enum Position {}
/// Defines units of velocity.
enum Velocity {}
/// Defines units of acceleration;
enum Acceleration {}
/// Defines units of force.
enum Force {}
/// Represents the concept of a particle in classical mechanics.
#[derive(Debug)]
struct Particle {
position: Vector3D::<f64, Position>,
velocity: Vector3D::<f64, Velocity>,
acceleration: Vector3D::<f64, Acceleration>,
mass: f64,
}
/// Represents an interaction between two particles, which will lead to a force.
#[derive(Debug)]
struct Interaction<'a> {
particle_1: &'a Particle,
particle_2: &'a Particle,
}
impl <'a> Interaction<'a> {
/// Computes the force acting on `particle_1` due to this interaction.
fn force_acting_on_particle_1(&self) -> Vector3D<f64,Force> {
let a = self.particle_1.position;
let b = self.particle_2.position;
Vector3D::<f64,Force>::new(b.x-a.x, b.y-a.y, b.z-a.z).normalize()
}
/// Computes the force acting on `particle_2` due to this interaction.
fn force_acting_on_particle_2(&self) -> Vector3D<f64,Force> {
self.force_acting_on_particle_1() * -1.
}
}
/// Represents a system of particles, i.e. a collection of particles that interact.
#[derive(Debug)]
struct ParticlesSystem<'a> {
particles: Vec::<Particle>,
interactions: Vec::<Interaction<'a>>,
}
impl <'a> ParticlesSystem<'a> {
/// Creates an empty particles system.
fn new() -> Self {
Self {
particles: Vec::<Particle>::new(),
interactions: Vec::<Interaction<'a>>::new(),
}
}
/// Add a particle to the system.
fn add_particle(&mut self, p: Particle) -> usize {
&self.particles.push(p);
self.particles.len()
}
/// Add an interaction between two particles of the system.
fn add_interaction(& mut self, p_id_a: usize, p_id_b: usize) {
let interaction = Interaction {
particle_1: &self.particles[p_id_a],
particle_2: &self.particles[p_id_b],
};
&self.interactions.push(interaction);
}
}
fn main() {
let mut system = ParticlesSystem::new();
let mut p = Particle {
position: Vector3D::<f64,Position>::new(-1.,0.,0.),
velocity: Vector3D::<f64,Velocity>::new(0.,0.,0.),
acceleration: Vector3D::<f64,Acceleration>::new(0.,0.,0.),
mass: 1.,
};
system.add_particle(p);
let mut p = Particle {
position: Vector3D::<f64,Position>::new(1.,0.,0.),
velocity: Vector3D::<f64,Velocity>::new(0.,0.,0.),
acceleration: Vector3D::<f64,Acceleration>::new(0.,0.,0.),
mass: 2.,
};
system.add_particle(p);
let mut p = Particle {
position: Vector3D::<f64,Position>::new(0.,1.,0.),
velocity: Vector3D::<f64,Velocity>::new(0.,0.,0.),
acceleration: Vector3D::<f64,Acceleration>::new(0.,0.,0.),
mass: 3.,
};
system.add_particle(p);
system.add_interaction(0,1);
system.add_interaction(0,2);
system.add_interaction(1,2);
dbg!(system);
}
which gives me this when I try to compile it:
error: lifetime may not live long enough
--> src/main.rs:102:4
|
83 | impl <'a> ParticlesSystem<'a> {
| -- lifetime `'a` defined here
...
97 | fn add_interaction(& mut self, p_id_a: usize, p_id_b: usize) {
| - let's call the lifetime of this reference `'1`
...
102 | &self.interactions.push(interaction);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a`
I think I understand the reason for this, so I modify the definition of add_interaction
as fn add_interaction(&'a mut self, p_id_a: usize, p_id_b: usize)
to tell the compiler self
will live at least as long as the interactions Interaction<'a>
. However, after this I get
error[E0499]: cannot borrow `system` as mutable more than once at a time
--> src/main.rs:97:5
|
96 | system.add_interaction(0,1);
| ------ first mutable borrow occurs here
97 | system.add_interaction(0,2);
| ^^^^^^
| |
| second mutable borrow occurs here
| first borrow later used here
How can I solve this in a way compatible with Rust?