-
Notifications
You must be signed in to change notification settings - Fork 220
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LazyUpdate: lazily insert and remove components, execute closures on world #221
Conversation
Looks ok to me. Thanks for the PR! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR! I'd prefer not to have a second struct actually, see below.
src/world.rs
Outdated
/// component storages have to be borrowed mutably. | ||
/// | ||
/// This resource is added to the world by default. | ||
pub struct LazyRemovals { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no reason to have a second struct, actually. Given that LazyInsertions
haven't been released yet, let's just rename it to LazyUpdate
and have a method there.
src/world.rs
Outdated
pub struct LazyInsertions { | ||
stack: TreiberStack<Box<LazyInsertInternal>> | ||
pub struct LazyUpdate { | ||
insert_stack: TreiberStack<Box<LazyInsertInternal>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's also rename LazyInsertInternal
to LazyUpdateInternal
so we do not need two stacks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To elaborate this a bit, LazyInsertInternal
can just access the World
, so we can just use it for both (after renaming). Just accept entities and a type parameter then, have a private struct with PhantomData
and implement the removal, please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, I will try!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good! I'd actually prefer to not have a tuple for removals, but that's up to you. I'll review it completely later.
src/world.rs
Outdated
fn insert(self: Box<Self>, world: &World); | ||
struct LazyRemovalMarker<F>(PhantomData<F>); | ||
|
||
impl<F> LazyRemovalMarker<F> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we just have a struct containing both the entity and the marker?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry - have to do some more nitpicking, I actually wonder if we should just allow lazy.execute(|world| world.hello())
, additionally. But that could also be done in another PR.
src/world.rs
Outdated
|
||
struct LazyRemovalInfo<C> { | ||
entity: Entity, | ||
marker: LazyRemovalMarker<C>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can actually inline that type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean using #[inline] on a get function (like with the Entity struct)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, just use PhantomData
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah right, of course!
src/world.rs
Outdated
} | ||
} | ||
|
||
/// Lazy updates can be used after creating a new |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say in general, they can be used for stuff that should better happen at the end because it needs to borrow many resources. Dealing with initialization of components could be shown as an example of that.
src/world.rs
Outdated
/// } | ||
/// } | ||
/// ``` | ||
pub fn add<L>(&self, l: L) | ||
pub fn add_insertions<L>(&self, l: L) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it would be better to do a "lazy insert" - so just pub fn insert
? Also, the plural is not always applicable.
src/world.rs
Outdated
|
||
/// Adds a removal. Please note that this method takes `&self` | ||
/// so there's no need to fetch it mutably. | ||
pub fn add_removal<C>(&self, e: Entity) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then this one would be just remove
.
src/world.rs
Outdated
where L: LazyInsert + 'static | ||
{ | ||
self.stack.push(Box::new(l)); | ||
} | ||
|
||
/// Adds a removal. Please note that this method takes `&self` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would also be nice to have a usage example somewhere.
Thanks for your feedback and no need to apologize, I agree with all points and I'm working on it! |
Also implemented execute. Tell me what you think, I'm happy to get your feedback. |
src/world.rs
Outdated
/// entity in a system. This way, none of the actual | ||
/// component storages have to be borrowed mutably. | ||
/// | ||
impl LazyUpdateInternal for Box<Fn(&World) + Send + Sync> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need Box
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure, I tried without that Box first, but ran into problems. The compiler suggests to add a 'static bound to F in execute, but the passed in closures apparently don't have 'static lifetime. Any idea how to solve this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you try impl<F> LazyUpdateInternal for F where F: Fn(&World) + Send + Sync + 'static
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, works!
src/world.rs
Outdated
self.stack.push(Box::new(LazyRemovalInfo::<C>(e, PhantomData))); | ||
} | ||
|
||
/// Lazily executes a closure with world access. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could also say that "lazily" means "when calling maintain()".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your work. I'm wondering if we need the LazyInsert
and the LazyRemoval
now that we have execute
.
src/world.rs
Outdated
where | ||
C: Component + Send + Sync, | ||
{ | ||
self.stack.push(Box::new(LazyRemovalInfo::<C>(e, PhantomData))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, this could just use execute
now. That way, there's no need for the removal struct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
src/world.rs
Outdated
/// } | ||
/// } | ||
/// ``` | ||
pub fn add<L>(&self, l: L) | ||
pub fn insert<L>(&self, l: L) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could probably divide this method into insert
and insert_all
to increase performance for multiple insertions; insert_all
could just accept an iterator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, will do that.
It would be nice if we could use execute in insert as well, to get rid of LazyInsert. But I think then FnOnce would be required, because the component would have to be moved into the closure, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I'm not sure, however, if executing call_once
on Box<FnOnce
is permitted / if there's a workaround.
Could you take a look? Still needs cleanup, docs and examples, just wanted to get some feedback at this point. |
Oops, forgot to remove LazyInsert.. |
Hm, would it make sense to rename |
src/world.rs
Outdated
self.stack.push(Box::new( | ||
Box::new(f) as Box<Fn(&World) + Send + Sync>) | ||
); | ||
self.stack.push(Box::new(f)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much better 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, we're nearly there. Just the few nits below - and please don't forget to update the changelog. Since this partially amends my PR, let's summarize it on one line and mention both PRs in brackets.
Really appreciating your efforts 👍
src/world.rs
Outdated
fn insert(self: Box<Self>, world: &World) { | ||
L::insert(*self, world); | ||
impl<F> LazyUpdateInternal for F | ||
where F: FnOnce(&World) + Send + Sync + 'static { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Formatting: {
is supposed to be on a new line.
src/world.rs
Outdated
pub struct LazyInsertions { | ||
stack: TreiberStack<Box<LazyInsertInternal>> | ||
/// Please note that the provided methods take `&self` | ||
/// so there's no need to fetch `LazyUpdate` mutably. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's leave the note that it is added to the world by default.
src/world.rs
Outdated
} | ||
}); | ||
} | ||
/// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing new line, docs and example usage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! These are great changes 👍
I guess we can merge this now. bors r+ |
Canceled |
Hold on, rebase is wrong bors r- |
Yes I was just about to fix the Changelog... |
The line in the changelog should be in the 0.9.3 section |
Yeah just committed... 😄 |
Okay, now it's perfect. bors r+ |
Thank you by the way for guiding me through this. It's actually my first PR and I learned a lot of things. I totally like that you want to get all the details right... it's a great thing to learn from such talented developers! 👍 |
Build succeeded |
/// ``` | ||
pub fn execute<F>(&self, f: F) | ||
where | ||
F: FnOnce(&World) + 'static + Send + Sync, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is there a Sync requirement here?
Remove a component lazily without having to borrow its storage. To be used like this:
let lazy = world.read_resource::<LazyUpdate>();
lazy.add_removal::<SomeComponentType>(some_entity);