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
Support closures as ComponentHooks #12287
base: main
Are you sure you want to change the base?
Conversation
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 good, the only reason I didn't do this in the first pass was because I wasn't sure about the potential for overhead, if this is the same for non-closure functions then it seems like a good change.
Thinking about this a bit more, the Arc may throw a wrench in that due to the extra metadata it has. It may be better to do this with a @SludgePhD filed #9812 which introduced the Clone impl, primarily for inspecting the ComponentInfo without a borrow on the World. The Clone impl could be removed and replaced with an equivalent extraction of the metadata (i.e. reading the information into a hypothetical |
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.
Had to ask for some language explanations on discord 😅
Looks good!
On further inspection, having |
Why not leave it as a function pointer for simplicity and minimal overhead? If you really need to store state you can always use a resource, though I feel like if you're doing anything complicated with a hook you should probably use an observer instead |
That's fair. Though I think this comes across as something most typical Rust users expect when working with callbacks. I've definitely found it rare to come across an API that takes a function as an argument and not have it take a closure of some kind. Perhaps we do need some sort of benchmarking to verify that this doesn't cost us anything extra when using normal function pointers. |
I mean, it does cost us extra, since the runtime needs to go through the vtable to find the address, rather than just directly using the function pointer. I don't think it will be a significant slowdown, but for something so low-level, why have more overhead when we can have less? |
@james7132 what do you think should be done with this, and how can I help? I'd like to get this into 0.14 to try and ship a nicer API in the first iteration of hooks. |
Objective
Component hooks right now only take raw function pointers instead of allowing those registering it to store some state with them.
Solution
Store an
Arc<dyn Fn(...)>
instead of a function pointer and make the registration functions generic over the function type being passed in.Right now this only takes read only closures (
Fn
notFnMut
), though strictly speaking we could support them as we have mutable access when triggering the hooks, accessing the closure mutably through an Arc would be troublesome if the ComponentInfo was cloned. I tried to keep the traits onComponentHooks
andComponentInfo
intact as much as I could.This shouldn't change the performance characteristics of the crate when not using a closure since
&dyn Fn(...)
directly includes the function pointer instead of being a double indirection.This PR also makes the type alias for ComponentHook private since it's now not part of the public interface of the crate.