SystemParamBuilder - Enable type inference of closure parameter when building dynamic systems (#14820)
# Objective When building a system from `SystemParamBuilder`s and defining the system as a closure, the compiler should be able to infer the parameter types from the builder types. ## Solution Create methods for each arity that take an argument that implements both `SystemParamFunction` as well as `FnMut(SystemParamItem<P>,...)`. The explicit `FnMut` constraint will allow the compiler to infer the necessary higher-ranked lifetimes along with the parameter types. I wanted to show that this was possible, but I can't tell whether it's worth the complexity. It requires a separate method for each arity, which pollutes the docs a bit:  ## Example ```rust let system = (LocalBuilder(0u64), ParamBuilder::local::<u64>()) .build_state(&mut world) .build_system(|a, b| *a + *b + 1); ```
This commit is contained in:
parent
8895113784
commit
419359b9a7
@ -457,6 +457,21 @@ mod tests {
|
|||||||
assert_eq!(result, 3);
|
assert_eq!(result, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multi_param_builder_inference() {
|
||||||
|
let mut world = World::new();
|
||||||
|
|
||||||
|
world.spawn(A);
|
||||||
|
world.spawn_empty();
|
||||||
|
|
||||||
|
let system = (LocalBuilder(0u64), ParamBuilder::local::<u64>())
|
||||||
|
.build_state(&mut world)
|
||||||
|
.build_system(|a, b| *a + *b + 1);
|
||||||
|
|
||||||
|
let result = world.run_system_once(system);
|
||||||
|
assert_eq!(result, 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn param_set_builder() {
|
fn param_set_builder() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
|||||||
@ -208,6 +208,52 @@ pub struct SystemState<Param: SystemParam + 'static> {
|
|||||||
archetype_generation: ArchetypeGeneration,
|
archetype_generation: ArchetypeGeneration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow closure arguments to be inferred.
|
||||||
|
// For a closure to be used as a `SystemParamFunction`, it needs to be generic in any `'w` or `'s` lifetimes.
|
||||||
|
// Rust will only infer a closure to be generic over lifetimes if it's passed to a function with a Fn constraint.
|
||||||
|
// So, generate a function for each arity with an explicit `FnMut` constraint to enable higher-order lifetimes,
|
||||||
|
// along with a regular `SystemParamFunction` constraint to allow the system to be built.
|
||||||
|
macro_rules! impl_build_system {
|
||||||
|
($($param: ident),*) => {
|
||||||
|
impl<$($param: SystemParam),*> SystemState<($($param,)*)> {
|
||||||
|
/// Create a [`FunctionSystem`] from a [`SystemState`].
|
||||||
|
/// This method signature allows type inference of closure parameters for a system with no input.
|
||||||
|
/// You can use [`SystemState::build_system_with_input()`] if you have input, or [`SystemState::build_any_system()`] if you don't need type inference.
|
||||||
|
pub fn build_system<
|
||||||
|
Out: 'static,
|
||||||
|
Marker,
|
||||||
|
F: FnMut($(SystemParamItem<$param>),*) -> Out
|
||||||
|
+ SystemParamFunction<Marker, Param = ($($param,)*), In = (), Out = Out>
|
||||||
|
>
|
||||||
|
(
|
||||||
|
self,
|
||||||
|
func: F,
|
||||||
|
) -> FunctionSystem<Marker, F>
|
||||||
|
{
|
||||||
|
self.build_any_system(func)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a [`FunctionSystem`] from a [`SystemState`].
|
||||||
|
/// This method signature allows type inference of closure parameters for a system with input.
|
||||||
|
/// You can use [`SystemState::build_system()`] if you have no input, or [`SystemState::build_any_system()`] if you don't need type inference.
|
||||||
|
pub fn build_system_with_input<
|
||||||
|
Input,
|
||||||
|
Out: 'static,
|
||||||
|
Marker,
|
||||||
|
F: FnMut(In<Input>, $(SystemParamItem<$param>),*) -> Out
|
||||||
|
+ SystemParamFunction<Marker, Param = ($($param,)*), In = Input, Out = Out>,
|
||||||
|
>(
|
||||||
|
self,
|
||||||
|
func: F,
|
||||||
|
) -> FunctionSystem<Marker, F> {
|
||||||
|
self.build_any_system(func)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
all_tuples!(impl_build_system, 0, 16, P);
|
||||||
|
|
||||||
impl<Param: SystemParam> SystemState<Param> {
|
impl<Param: SystemParam> SystemState<Param> {
|
||||||
/// Creates a new [`SystemState`] with default state.
|
/// Creates a new [`SystemState`] with default state.
|
||||||
///
|
///
|
||||||
@ -242,7 +288,9 @@ impl<Param: SystemParam> SystemState<Param> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a [`FunctionSystem`] from a [`SystemState`].
|
/// Create a [`FunctionSystem`] from a [`SystemState`].
|
||||||
pub fn build_system<Marker, F: SystemParamFunction<Marker, Param = Param>>(
|
/// This method signature allows any system function, but the compiler will not perform type inference on closure parameters.
|
||||||
|
/// You can use [`SystemState::build_system()`] or [`SystemState::build_system_with_input()`] to get type inference on parameters.
|
||||||
|
pub fn build_any_system<Marker, F: SystemParamFunction<Marker, Param = Param>>(
|
||||||
self,
|
self,
|
||||||
func: F,
|
func: F,
|
||||||
) -> FunctionSystem<Marker, F> {
|
) -> FunctionSystem<Marker, F> {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user