bevy_reflect: Add Function trait
#15205
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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
DynamicFunctiontodyn PartialReflect.Solution
Allow
DynamicFunctionto actually be reflected.This PR adds the
Functionreflection subtrait (and correspondingReflectRef,ReflectKind, etc.). With this new trait, we're able to implementPartialReflectonDynamicFunction.Implementors
Functionis 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 implementFunctionforDynamicFunctionMut.The reasons for this are a little complicated, but it boils down to mutability.
DynamicFunctionMutrequires&mut selfto be invoked since it wraps aFnMut. However, we can't really model this well withFunction. And if we makeDynamicFunctionMutwrap its internalFnMutin aMutexto allow for&selfinvocations, 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
FunctionforDynamicFunctionMut. 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
DynamicFunctionis the fact that it's both a dynamic representation (likeDynamicStructorDynamicList) 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 staticTypeInfocould 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_applyimplementation since it would have to clone anyways, but is definitely not ideal. This is also the reason I had to addDebugas a supertrait onFunction.For now, this PR chooses not to implement
ReflectforDynamicFunction. 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
DynamicFunctionas 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).