-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
bevy_reflect: Add Function
trait
#15205
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
bevy_reflect: Add Function
trait
#15205
Conversation
Currently only implemented for DynamicFunction
Needed to get try_apply to work on DynamicFunction
8186212
to
15a5835
Compare
5ca983a
to
c9dfa60
Compare
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.
Very cool, makes sense, and my mind is already jumping with possibilities... proxied DynamicStruct
s with callable fields that can change at runtime? My sense is that, in this respect, DynamicFunctionMut
is going to be desirable, even if it requires some gotchas around reentrancy, etc.
Objective
While #13152 added function reflection, it didn't really make functions reflectable. Instead, it made it so that they can be called with reflected arguments and return reflected data. But functions themselves cannot be reflected.
In other words, we can't go from
DynamicFunction
todyn PartialReflect
.Solution
Allow
DynamicFunction
to actually be reflected.This PR adds the
Function
reflection subtrait (and correspondingReflectRef
,ReflectKind
, etc.). With this new trait, we're able to implementPartialReflect
onDynamicFunction
.Implementors
Function
is currently only implemented forDynamicFunction<'static>
. This is because we can't implement it generically over all functions—even those that implementIntoFunction
.What about
DynamicFunctionMut
? Well, this PR does not implementFunction
forDynamicFunctionMut
.The reasons for this are a little complicated, but it boils down to mutability.
DynamicFunctionMut
requires&mut self
to be invoked since it wraps aFnMut
. However, we can't really model this well withFunction
. And if we makeDynamicFunctionMut
wrap its internalFnMut
in aMutex
to allow for&self
invocations, then we run into either concurrency issues or recursion issues (or, in the worst case, both).So for the time-being, we won't implement
Function
forDynamicFunctionMut
. It will be better to evaluate it on its own. And we may even consider the possibility of removing it altogether if it adds too much complexity to the crate.Dynamic vs Concrete
One of the issues with
DynamicFunction
is the fact that it's both a dynamic representation (likeDynamicStruct
orDynamicList
) and the only way to represent a function.Because of this, it's in a weird middle ground where we can't easily implement full-on
Reflect
. That would requireTyped
, but what staticTypeInfo
could it provide? Just that it's aDynamicFunction
? None of the other dynamic types implementTyped
.However, by not implementing
Reflect
, we lose the ability to downcast back to ourDynamicStruct
. Our only option is to callFunction::clone_dynamic
, which clones the data rather than by simply downcasting. This works in favor of thePartialReflect::try_apply
implementation since it would have to clone anyways, but is definitely not ideal. This is also the reason I had to addDebug
as a supertrait onFunction
.For now, this PR chooses not to implement
Reflect
forDynamicFunction
. We may want to explore this in a followup PR (or even this one if people feel strongly that it's strictly required).The same is true for
FromReflect
. We may decide to add an implementation there as well, but it's likely out-of-scope of this PR.Testing
You can test locally by running:
Showcase
You can now pass around a
DynamicFunction
as adyn PartialReflect
! This also means you can use it as a field on a reflected type without having to ignore it (though you do need to opt out ofFromReflect
).