The specs crate has a Join
trait that allows you to iterate over groups of components. The problem is that in order to iterate over components, you need to retrieve the storage for that component in a separate step from actually "joining" the components.
Here's an example of what I mean. It does not compile.
// Rust 2018 edition
use specs::{World, Component, VecStorage, ReadStorage, Join};
use specs_derive::Component;
#[derive(Debug, Component)]
#[storage(VecStorage)]
pub struct Position {x: i32, y: i32}
#[derive(Debug, Component)]
#[storage(VecStorage)]
pub struct Velocity {x: i32, y: i32}
// Goal: A function with a signature resembling the following:
fn iter_components(world: &World) -> impl Iterator<Item=(&Position, &Velocity)> {
// Gets the storages of both components and puts them into these two variables on the stack (important!)
let (positions, velocities) = world.system_data::<(ReadStorage<Position>, ReadStorage<Velocity>)>();
// Borrows these two variables on the stack
// Need to do this because Join is only implemented on &ReadStorage<T>
(&positions, &velocities).join()
} // positions and velocities get dropped here, so this won't compile (they are still borrowed)
This gives you the error:
error[E0515]: cannot return value referencing local variable `velocities`
--> src/main.rs:22:5
|
22 | (&positions, &velocities).join()
| ^^^^^^^^^^^^^-----------^^^^^^^^
| | |
| | `velocities` is borrowed here
| returns a value referencing data owned by the current function
error[E0515]: cannot return value referencing local variable `positions`
--> src/main.rs:22:5
|
22 | (&positions, &velocities).join()
| ^----------^^^^^^^^^^^^^^^^^^^^^
| ||
| |`positions` is borrowed here
| returns a value referencing data owned by the current function
I understand exactly why this error is occurring. Is there any way to deal with this given the API that specs provides?
Usually, the solution is to move the values that are being dropped into a struct and return the struct. The problem is that to do that you would have to store both the storages (positions
, velocities
) and the iterator (&positions, &velocities).join()
. If you did that however, you would run into another problem with the fields referring to other fields in the same struct (i.e. a self-referential struct).