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); | ||||
|     } | ||||
| 
 | ||||
|     #[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] | ||||
|     fn param_set_builder() { | ||||
|         let mut world = World::new(); | ||||
|  | ||||
| @ -208,6 +208,52 @@ pub struct SystemState<Param: SystemParam + 'static> { | ||||
|     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> { | ||||
|     /// Creates a new [`SystemState`] with default state.
 | ||||
|     ///
 | ||||
| @ -242,7 +288,9 @@ impl<Param: SystemParam> SystemState<Param> { | ||||
|     } | ||||
| 
 | ||||
|     /// 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, | ||||
|         func: F, | ||||
|     ) -> FunctionSystem<Marker, F> { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Chris Russell
						Chris Russell