Hello all,
I'm trying to learn Rust and in order to do so I'm trying to write an n body simulation (originally implemented/prototypes in Python, but it doesn't scale well at all).
The issue is I can't seem to get the same structure of code to work in Rust as it does in Python. I believe my understanding of Lifecycles in Rust is my weak point here.
A stripped down example of the Rust and Python versions (tried to make it a 1-1 comparison),
Python:
from typing import List
from dataclasses import dataclass
import random
@dataclass
class Position:
x: float
y: float
@dataclass
class Entity:
position: Position
def __init__(self):
self.position = Position(
x=round(random.random(), 4),
y=round(random.random(), 4)
)
@dataclass
class EntityNearestNeighbors:
entity: Entity
nearest_neighbours: List[Entity]
def __init__(self, entity: Entity):
self.entity = entity
def set_closest_nearest_neighbours(self, all_entities):
# calculate the distances
distances_with_entity = []
for e in all_entities:
dist = distance_between_positions(self.entity.position, e.position)
dist_with_entity = (dist, e)
distances_with_entity.append(dist_with_entity)
# sort by distance
distances_with_entity.sort(key=lambda x: x[0])
# extract out entities from nearest to furthest
nearest_neighbors = []
for dist, e in distances_with_entity:
nearest_neighbors.append(e)
# set the ordered entities
self.nearest_neighbours = nearest_neighbors
def distance_between_positions(pos_1: Position, pos_2: Position) -> float:
x_dist = pos_1.x - pos_2.x
y_dist = pos_1.y - pos_2.y
distance = (x_dist ** 2 + y_dist ** 2) ** 0.5
return distance
if __name__ == '__main__':
# toy example, so only making 3 entites
number_of_entities = 3
all_entities = [Entity() for _ in range(number_of_entities)]
# print(all_entities)
all_entity_areas = [EntityNearestNeighbors(e) for e in all_entities]
for entity_area in all_entity_areas:
entity_area.set_closest_nearest_neighbours(all_entities)
print(f"Entity: {all_entity_areas[0].entity}, nearest entity: {all_entity_areas[0].nearest_neighbours[0]}")
Rust:
use rand::prelude::*;
#[derive(Debug)]
struct Position {
x: f32,
y: f32,
}
#[derive(Debug)]
struct Entity {
position: Position,
}
struct EntityNearestNeighbors<'a> {
entity: Entity,
nearest_neighbours: Vec<&'a Entity>,
}
fn distance_between_positions(pos_1: &Position, pos_2: &Position) -> f32 {
let x_dist = pos_1.x - pos_2.x;
let y_dist = pos_1.y - pos_2.y;
let distance = f32::powf(f32::powf(x_dist, 2.) + f32::powf(y_dist, 2.), 0.5);
distance
}
impl Entity {
fn new() -> Entity {
let position = Position {
y: random(),
x: random(),
};
Entity {
position: position,
}
}
}
impl EntityNearestNeighbors<'_> {
fn new(entity: Entity) -> EntityNearestNeighbors<'static> {
let position = Position {
y: random(),
x: random(),
};
EntityNearestNeighbors {
entity: entity,
nearest_neighbours: vec![],
}
}
fn set_closest_nearest_neighbours(&mut self, all_entities: Vec<&Entity>) {
// calculate the distances
let mut distances_with_entity: Vec<(f32, &Entity)> = Vec::new();
for e in all_entities {
let dist = distance_between_positions(&self.entity.position, &e.position);
let dist_with_entity = (dist, e);
distances_with_entity.push(dist_with_entity);
}
// sort by distance
distances_with_entity.sort_unstable_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
// extract out entities from nearest to furthest
let nearest_neighbours = vec![];
for (dist, e) in distances_with_entity.iter() {
nearest_neighbours.push(*e);
}
// set the ordered entities
self.nearest_neighbours = nearest_neighbours;
}
}
fn main() {
// toy example, so only making 3 entities
let number_of_entities = 3;
let all_entities = (1..(number_of_entities+1)).map(|_| Entity::new()).collect::<Vec<Entity>>();
println!("{:?}", all_entities);
let mut all_entity_areas = all_entities.iter().map(|e| EntityNearestNeighbors::new(*e)).collect::<Vec<EntityNearestNeighbors>>();
let all_entities_references = all_entities.iter().collect();
for entity_area in all_entity_areas.iter() {
entity_area.set_closest_nearest_neighbours(all_entities_references);
}
println!("Entity: {:?}, nearest entity: {:?}", all_entity_areas[0].entity, all_entity_areas[0].nearest_neighbours[0]);
}
The compiler is giving this error:
Compiling parallel_struct_test v0.1.0 (/home/sampur01/Downloads/leetcode/parallel_struct_test)
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:54:18
|
54 | for e in all_entities {
| ^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 50:68...
--> src/main.rs:50:68
|
50 | fn set_closest_nearest_neighbours(&mut self, all_entities: Vec<&Entity>) {
| ^^^^^^^
note: ...but the lifetime must also be valid for the lifetime `'_` as defined on the impl at 38:29...
--> src/main.rs:38:29
|
38 | impl EntityNearestNeighbors<'_> {
| ^^
note: ...so that the expression is assignable
--> src/main.rs:54:18
|
54 | for e in all_entities {
| ^^^^^^^^^^^^
= note: expected `Vec<&Entity>`
found `Vec<&Entity>`
For more information about this error, try `rustc --explain E0495`.
error: could not compile `parallel_struct_test` due to previous error
I'm struggling to understand the issue, because surely the lifetime of the reference to each entity
in entity_area
would only exist for as long as entity_area
exists, which in this case is until the end of the program.
Any advice on what I'm doing wrong would be greatly appreciated.