Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upLifetime issues with component storages #519
Comments
This comment has been minimized.
This comment has been minimized.
|
Option 1 is much more serviceable in my opinion. |
This comment has been minimized.
This comment has been minimized.
sunjay
commented
Dec 12, 2018
I'm happy with either. I don't actually know the internals well so my suggestion was just a guess :) |
This comment has been minimized.
This comment has been minimized.
|
Thanks for filing this @sunjay! I don't see a good way to implement Option 1. These values need to be held in memory because they protect memory from being accessed in an invalid manner. What you can do though: struct Iter<'a> {
pos: WriteStorage<'a, Position>,
vel: ReadStorage<'a, Velocity>,
}
impl<'a> Iter<'a> {
fn iter(&mut self) -> impl Iterator<Item = (&mut Position, &Velocity)> + 'a {
(&mut self.pos, &self.vel).join()
}
}
fn iter_components<'a>(world: &'a World) -> Iter<'a> {
let (pos, vel) = world.system_data::<(WriteStorage<Position>, WriteStorage<Velocity>)>();
Iter {
pos, vel,
}
}
// Then:
iter_components(&world).iter().do_whatever();This works because Rust will just keep the value in memory for as long as necessary and drop it once it's not borrowed anymore (so basically create the temporary for you). As for the second option, that could be implemented, yes. I share @Xaeroxe's concern though. I don't quite know what you're trying to achieve, but what I do usually is: world.exec(|(vel: ReadStorage<Velocity>, pos: WriteStorage<Position>)| {
(&vel, &mut pos)
.join()
.do_something()
});If you want to hide the world and you plan on creating more of those convenience functions, one thing you can do is create |
This comment has been minimized.
This comment has been minimized.
sunjay
commented
Dec 13, 2018
•
I have a bunch of components that I want to move from one My goal is to be able to write a function with a signature similar to: fn iter_components<'a>(world: &'a World) -> impl Iterator<Item=(&mut Position, &Velocity)> + 'a
Could something like |
sunjay commentedDec 12, 2018
•
edited
I originally posted about this in the users forum and then on Reddit.
Terminology Note: When I say "owned X" in the text below, I mean that you have ownership of a value X of type
T, not a reference to that value (e.g.&Tor&mut T)You can look at this issue from two perspectives:
SystemDatacontaining references to storages.Example: You can get
(ReadStorage<A>, ReadStorage<B>)by calling thesystem_datamethod onWorldbut not(&ReadStorage<A>, &ReadStorage<B>)Jointrait with the owned versions of component storages.Example: You can do
(&a, &b).join()but not(a, b).join()Both of these issues exist for good reasons, but they make code like the following very hard to write:
This doesn't work and you get the following error: (copied from users forum post, see that for the full code example)
If either of the two things I listed above weren't the case, this code could potentially compile:
Jointrait on owned versions of the storages:A note about this:
ReadStoragemakes it pretty obvious that we are immutably borrowing here, but withWriteStoragewe would probably need some methods likeread_owned()andwrite_owned()to emulate what you could do with&and&mutbefore. See the following code for more details:I think this solution is probably the easiest to implement because it doesn't involve the
Worldhaving to hold on to arbitrary storages for some extended amount of time.The API I would suggest is:
ReadStorage<T>andWriteStorage<T>implementJoinand produce&TWriteStorage<T>provides a method (e.g.write_owned()) that returns an owned value that implementsJoinand produces&mut T(perhaps something similar toMaybeJoin?)This may cause issues with
Worldbecause it's probably not a good thing if a storage lives longer than expected. I am not familiar enough with the internals to comment on that.