I'm trying to make a small game in Rust. I want to use something similar to the entity-component-system pattern to handle all the game objects.
My general idea is to have a GameLoop
struct which holds all the necessary data to update and draw the game (the screen, a timestamp, ...).
The World
struct is supposed to hold all the game entities and update them in the dispatch
function. It calls all the registered callbacks which are stored in the World
struct as well (those are the "systems"). They're redundant in the sample code, though.
I've tried to break down the code as much as possible and only include the relevant parts.
use std::marker::PhantomData;
struct Something;
///The "somethings" are things like the display, a timestamp, ...
struct GameLoop {
sth: Something,
sth2: Something,
}
///C = Context
///The type that is passed to every entity to give it access to things like the delta time
struct World<C> {
phantom: PhantomData<C>, //This is here so Rust doesn't complain about the type parameter not being used
}
///The data that is passed to the system functions every frame
struct TickData<'a> {
delta: u64,
sth: &'a Something,
sth2: &'a mut Something,
}
impl GameLoop {
fn new() -> GameLoop {
GameLoop {
sth: Something {},
sth2: Something {},
}
}
///One game "tick" - Supposed to do things like calculating delta time, swapping buffers, ...
///Those are then passed to the callback
fn update<F: FnMut(u64, &Something, &mut Something)>(&mut self, f: &mut F) {
f(0, &self.sth, &mut self.sth2);
}
}
impl<C> World<C> {
fn new() -> World<C> {
World { phantom: PhantomData }
}
///Supposed to update all the game entities
fn dispatch(&mut self, context: &mut C) {
//...
}
}
impl<'a> TickData<'a> {
fn new<'b>(delta: u64, sth: &'b Something, sth2: &'b mut Something) -> TickData<'b> {
TickData {
delta: delta,
sth: sth,
sth2: sth2,
}
}
}
fn main() {
let mut game_loop = GameLoop::new();
let mut world = World::<TickData>::new();
//The game update function, called once per frame
let mut update_fnc = |delta: u64, sth: &Something, sth2: &mut Something| {
let mut tick_data = TickData::new(delta, sth, sth2);
&world.dispatch(&mut tick_data); //If this line is commented out, it compiles fine
//...
};
loop {
game_loop.update(&mut update_fnc); //Calculate the delta time, call the specified function and swap buffers
}
}
There seems to be a problem with borrowing / lifetimes. The compiler is everything else but verbose.
The problem seems to be the &world.dispatch(&mut tick_data)
call in the update function of the game, which is supposed to update all the game entities. If I comment it out the program compiles without any errors.
This is what the compiler tells me:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter 'b in function call due to conflicting requirements
--> src/main.rs:66:29
|
66 | let mut tick_data = TickData::new(delta, sth, sth2);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 65:77...
--> src/main.rs:65:78
|
65 | let mut update_fnc = |delta: u64, sth: &Something, sth2: &mut Something| {
| ______________________________________________________________________________^ starting here...
66 | | let mut tick_data = TickData::new(delta, sth, sth2);
67 | |
68 | | &world.dispatch(&mut tick_data); //If this line is commented out, it compiles fine
69 | |
70 | | //...
71 | | };
| |_____^ ...ending here
note: ...so that reference does not outlive borrowed content
--> src/main.rs:66:55
|
66 | let mut tick_data = TickData::new(delta, sth, sth2);
| ^^^^
note: but, the lifetime must be valid for the expression at 74:25...
--> src/main.rs:74:26
|
74 | game_loop.update(&mut update_fnc); //Calculate the delta time, call the specified function and swap buffers
| ^^^^^^^^^^^^^^^
note: ...so that reference is valid at the time of borrow
--> src/main.rs:74:26
|
74 | game_loop.update(&mut update_fnc); //Calculate the delta time, call the specified function and swap buffers
| ^^^^^^^^^^^^^^^
I simply can't spot the cause of the error. The functions get called in a kind of procedural way and since I'm only borrowing most of the data there should be no problem with the lifetimes.
When I remove the references from the TickData
struct so it only contains values that are implemented for the Copy
trait, it works as well.
I'm usually not the kind of person to post a wall of code and ask people to fix it but I'm really clueless right now.