 6ab8767d3b
			
		
	
	
		6ab8767d3b
		
			
		
	
	
	
	
		
			
			# Objective
- Implements the [Unique Reflect
RFC](https://github.com/nicopap/rfcs/blob/bevy-reflect-api/rfcs/56-better-reflect.md).
## Solution
- Implements the RFC.
- This implementation differs in some ways from the RFC:
- In the RFC, it was suggested `Reflect: Any` but `PartialReflect:
?Any`. During initial implementation I tried this, but we assume the
`PartialReflect: 'static` in a lot of places and the changes required
crept out of the scope of this PR.
- `PartialReflect::try_into_reflect` originally returned `Option<Box<dyn
Reflect>>` but i changed this to `Result<Box<dyn Reflect>, Box<dyn
PartialReflect>>` since the method takes by value and otherwise there
would be no way to recover the type. `as_full` and `as_full_mut` both
still return `Option<&(mut) dyn Reflect>`.
---
## Changelog
- Added `PartialReflect`.
- `Reflect` is now a subtrait of `PartialReflect`.
- Moved most methods on `Reflect` to the new `PartialReflect`.
- Added `PartialReflect::{as_partial_reflect, as_partial_reflect_mut,
into_partial_reflect}`.
- Added `PartialReflect::{try_as_reflect, try_as_reflect_mut,
try_into_reflect}`.
- Added `<dyn PartialReflect>::{try_downcast_ref, try_downcast_mut,
try_downcast, try_take}` supplementing the methods on `dyn Reflect`.
## Migration Guide
- Most instances of `dyn Reflect` should be changed to `dyn
PartialReflect` which is less restrictive, however trait bounds should
generally stay as `T: Reflect`.
- The new `PartialReflect::{as_partial_reflect, as_partial_reflect_mut,
into_partial_reflect, try_as_reflect, try_as_reflect_mut,
try_into_reflect}` methods as well as `Reflect::{as_reflect,
as_reflect_mut, into_reflect}` will need to be implemented for manual
implementors of `Reflect`.
## Future Work
- This PR is designed to be followed up by another "Unique Reflect Phase
2" that addresses the following points:
- Investigate making serialization revolve around `Reflect` instead of
`PartialReflect`.
- [Remove the `try_*` methods on `dyn PartialReflect` since they are
stop
gaps](https://github.com/bevyengine/bevy/pull/7207#discussion_r1083476050).
- Investigate usages like `ReflectComponent`. In the places they
currently use `PartialReflect`, should they be changed to use `Reflect`?
- Merging this opens the door to lots of reflection features we haven't
been able to implement.
- We could re-add [the `Reflectable`
trait](8e3488c880/crates/bevy_reflect/src/reflect.rs (L337-L342))
and make `FromReflect` a requirement to improve [`FromReflect`
ergonomics](https://github.com/bevyengine/rfcs/pull/59). This is
currently not possible because dynamic types cannot sensibly be
`FromReflect`.
  - Since this is an alternative to #5772, #5781 would be made cleaner.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
		
	
			
		
			
				
	
	
		
			181 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			181 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! This example demonstrates how functions can be called dynamically using reflection.
 | |
| //!
 | |
| //! Function reflection is useful for calling regular Rust functions in a dynamic context,
 | |
| //! where the types of arguments, return values, and even the function itself aren't known at compile time.
 | |
| //!
 | |
| //! This can be used for things like adding scripting support to your application,
 | |
| //! processing deserialized reflection data, or even just storing type-erased versions of your functions.
 | |
| 
 | |
| use bevy::reflect::func::{
 | |
|     ArgList, DynamicClosure, DynamicClosureMut, DynamicFunction, FunctionError, FunctionInfo,
 | |
|     IntoClosure, IntoClosureMut, IntoFunction, Return,
 | |
| };
 | |
| use bevy::reflect::{PartialReflect, Reflect};
 | |
| 
 | |
| // Note that the `dbg!` invocations are used purely for demonstration purposes
 | |
| // and are not strictly necessary for the example to work.
 | |
| fn main() {
 | |
|     // There are times when it may be helpful to store a function away for later.
 | |
|     // In Rust, we can do this by storing either a function pointer or a function trait object.
 | |
|     // For example, say we wanted to store the following function:
 | |
|     fn add(left: i32, right: i32) -> i32 {
 | |
|         left + right
 | |
|     }
 | |
| 
 | |
|     // We could store it as either of the following:
 | |
|     let fn_pointer: fn(i32, i32) -> i32 = add;
 | |
|     let fn_trait_object: Box<dyn Fn(i32, i32) -> i32> = Box::new(add);
 | |
| 
 | |
|     // And we can call them like so:
 | |
|     let result = fn_pointer(2, 2);
 | |
|     assert_eq!(result, 4);
 | |
|     let result = fn_trait_object(2, 2);
 | |
|     assert_eq!(result, 4);
 | |
| 
 | |
|     // However, you'll notice that we have to know the types of the arguments and return value at compile time.
 | |
|     // This means there's not really a way to store or call these functions dynamically at runtime.
 | |
|     // Luckily, Bevy's reflection crate comes with a set of tools for doing just that!
 | |
|     // We do this by first converting our function into the reflection-based `DynamicFunction` type
 | |
|     // using the `IntoFunction` trait.
 | |
|     let function: DynamicFunction = dbg!(add.into_function());
 | |
| 
 | |
|     // This time, you'll notice that `DynamicFunction` doesn't take any information about the function's arguments or return value.
 | |
|     // This is because `DynamicFunction` checks the types of the arguments and return value at runtime.
 | |
|     // Now we can generate a list of arguments:
 | |
|     let args: ArgList = dbg!(ArgList::new().push_owned(2_i32).push_owned(2_i32));
 | |
| 
 | |
|     // And finally, we can call the function.
 | |
|     // This returns a `Result` indicating whether the function was called successfully.
 | |
|     // For now, we'll just unwrap it to get our `Return` value,
 | |
|     // which is an enum containing the function's return value.
 | |
|     let return_value: Return = dbg!(function.call(args).unwrap());
 | |
| 
 | |
|     // The `Return` value can be pattern matched or unwrapped to get the underlying reflection data.
 | |
|     // For the sake of brevity, we'll just unwrap it here and downcast it to the expected type of `i32`.
 | |
|     let value: Box<dyn PartialReflect> = return_value.unwrap_owned();
 | |
|     assert_eq!(value.try_take::<i32>().unwrap(), 4);
 | |
| 
 | |
|     // The same can also be done for closures that capture references to their environment.
 | |
|     // Closures that capture their environment immutably can be converted into a `DynamicClosure`
 | |
|     // using the `IntoClosure` trait.
 | |
|     let minimum = 5;
 | |
|     let clamp = |value: i32| value.max(minimum);
 | |
| 
 | |
|     let function: DynamicClosure = dbg!(clamp.into_closure());
 | |
|     let args = dbg!(ArgList::new().push_owned(2_i32));
 | |
|     let return_value = dbg!(function.call(args).unwrap());
 | |
|     let value: Box<dyn PartialReflect> = return_value.unwrap_owned();
 | |
|     assert_eq!(value.try_take::<i32>().unwrap(), 5);
 | |
| 
 | |
|     // We can also handle closures that capture their environment mutably
 | |
|     // using the `IntoClosureMut` trait.
 | |
|     let mut count = 0;
 | |
|     let increment = |amount: i32| count += amount;
 | |
| 
 | |
|     let closure: DynamicClosureMut = dbg!(increment.into_closure_mut());
 | |
|     let args = dbg!(ArgList::new().push_owned(5_i32));
 | |
|     // Because `DynamicClosureMut` mutably borrows `total`,
 | |
|     // it will need to be dropped before `total` can be accessed again.
 | |
|     // This can be done manually with `drop(closure)` or by using the `DynamicClosureMut::call_once` method.
 | |
|     dbg!(closure.call_once(args).unwrap());
 | |
|     assert_eq!(count, 5);
 | |
| 
 | |
|     // As stated before, this works for many kinds of simple functions.
 | |
|     // Functions with non-reflectable arguments or return values may not be able to be converted.
 | |
|     // Generic functions are also not supported (unless manually monomorphized like `foo::<i32>.into_function()`).
 | |
|     // Additionally, the lifetime of the return value is tied to the lifetime of the first argument.
 | |
|     // However, this means that many methods (i.e. functions with a `self` parameter) are also supported:
 | |
|     #[derive(Reflect, Default)]
 | |
|     struct Data {
 | |
|         value: String,
 | |
|     }
 | |
| 
 | |
|     impl Data {
 | |
|         fn set_value(&mut self, value: String) {
 | |
|             self.value = value;
 | |
|         }
 | |
| 
 | |
|         // Note that only `&'static str` implements `Reflect`.
 | |
|         // To get around this limitation we can use `&String` instead.
 | |
|         fn get_value(&self) -> &String {
 | |
|             &self.value
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     let mut data = Data::default();
 | |
| 
 | |
|     let set_value = dbg!(Data::set_value.into_function());
 | |
|     let args = dbg!(ArgList::new().push_mut(&mut data)).push_owned(String::from("Hello, world!"));
 | |
|     dbg!(set_value.call(args).unwrap());
 | |
|     assert_eq!(data.value, "Hello, world!");
 | |
| 
 | |
|     let get_value = dbg!(Data::get_value.into_function());
 | |
|     let args = dbg!(ArgList::new().push_ref(&data));
 | |
|     let return_value = dbg!(get_value.call(args).unwrap());
 | |
|     let value: &dyn PartialReflect = return_value.unwrap_ref();
 | |
|     assert_eq!(value.try_downcast_ref::<String>().unwrap(), "Hello, world!");
 | |
| 
 | |
|     // Lastly, for more complex use cases, you can always create a custom `DynamicFunction` manually.
 | |
|     // This is useful for functions that can't be converted via the `IntoFunction` trait.
 | |
|     // For example, this function doesn't implement `IntoFunction` due to the fact that
 | |
|     // the lifetime of the return value is not tied to the lifetime of the first argument.
 | |
|     fn get_or_insert(value: i32, container: &mut Option<i32>) -> &i32 {
 | |
|         if container.is_none() {
 | |
|             *container = Some(value);
 | |
|         }
 | |
| 
 | |
|         container.as_ref().unwrap()
 | |
|     }
 | |
| 
 | |
|     let get_or_insert_function = dbg!(DynamicFunction::new(
 | |
|         |mut args| {
 | |
|             // We can optionally add a check to ensure we were given the correct number of arguments.
 | |
|             if args.len() != 2 {
 | |
|                 return Err(FunctionError::ArgCountMismatch {
 | |
|                     expected: 2,
 | |
|                     received: args.len(),
 | |
|                 });
 | |
|             }
 | |
| 
 | |
|             // The `ArgList` contains the arguments in the order they were pushed.
 | |
|             // We can retrieve them out in order (note that this modifies the `ArgList`):
 | |
|             let value = args.take::<i32>()?;
 | |
|             let container = args.take::<&mut Option<i32>>()?;
 | |
| 
 | |
|             // We could have also done the following to make use of type inference:
 | |
|             // let value = args.take_owned()?;
 | |
|             // let container = args.take_mut()?;
 | |
| 
 | |
|             Ok(Return::Ref(get_or_insert(value, container)))
 | |
|         },
 | |
|         // Functions can be either anonymous or named.
 | |
|         // It's good practice, though, to try and name your functions whenever possible.
 | |
|         // This makes it easier to debug and is also required for function registration.
 | |
|         // We can either give it a custom name or use the function's type name as
 | |
|         // derived from `std::any::type_name_of_val`.
 | |
|         FunctionInfo::named(std::any::type_name_of_val(&get_or_insert))
 | |
|             // We can always change the name if needed.
 | |
|             // It's a good idea to also ensure that the name is unique,
 | |
|             // such as by using its type name or by prefixing it with your crate name.
 | |
|             .with_name("my_crate::get_or_insert")
 | |
|             // Since our function takes arguments, we should provide that argument information.
 | |
|             // This helps ensure that consumers of the function can validate the arguments they
 | |
|             // pass into the function and helps for debugging.
 | |
|             // Arguments should be provided in the order they are defined in the function.
 | |
|             .with_arg::<i32>("value")
 | |
|             .with_arg::<&mut Option<i32>>("container")
 | |
|             // We can provide return information as well.
 | |
|             .with_return::<&i32>(),
 | |
|     ));
 | |
| 
 | |
|     let mut container: Option<i32> = None;
 | |
| 
 | |
|     let args = dbg!(ArgList::new().push_owned(5_i32).push_mut(&mut container));
 | |
|     let value = dbg!(get_or_insert_function.call(args).unwrap()).unwrap_ref();
 | |
|     assert_eq!(value.try_downcast_ref::<i32>(), Some(&5));
 | |
| 
 | |
|     let args = dbg!(ArgList::new().push_owned(500_i32).push_mut(&mut container));
 | |
|     let value = dbg!(get_or_insert_function.call(args).unwrap()).unwrap_ref();
 | |
|     assert_eq!(value.try_downcast_ref::<i32>(), Some(&5));
 | |
| }
 |