 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>
		
	
			
		
			
				
	
	
		
			129 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			129 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use crate::{FromType, PartialReflect, Reflect};
 | |
| 
 | |
| /// A trait that enables types to be dynamically constructed from reflected data.
 | |
| ///
 | |
| /// It's recommended to use the [derive macro] rather than manually implementing this trait.
 | |
| ///
 | |
| /// `FromReflect` allows dynamic proxy types, like [`DynamicStruct`], to be used to generate
 | |
| /// their concrete counterparts.
 | |
| /// It can also be used to partially or fully clone a type (depending on whether it has
 | |
| /// ignored fields or not).
 | |
| ///
 | |
| /// In some cases, this trait may even be required.
 | |
| /// Deriving [`Reflect`] on an enum requires all its fields to implement `FromReflect`.
 | |
| /// Additionally, some complex types like `Vec<T>` require that their element types
 | |
| /// implement this trait.
 | |
| /// The reason for such requirements is that some operations require new data to be constructed,
 | |
| /// such as swapping to a new variant or pushing data to a homogeneous list.
 | |
| ///
 | |
| /// See the [crate-level documentation] to see how this trait can be used.
 | |
| ///
 | |
| /// [derive macro]: bevy_reflect_derive::FromReflect
 | |
| /// [`DynamicStruct`]: crate::DynamicStruct
 | |
| /// [crate-level documentation]: crate
 | |
| #[diagnostic::on_unimplemented(
 | |
|     message = "`{Self}` can not be created through reflection",
 | |
|     note = "consider annotating `{Self}` with `#[derive(FromReflect)]`"
 | |
| )]
 | |
| pub trait FromReflect: Reflect + Sized {
 | |
|     /// Constructs a concrete instance of `Self` from a reflected value.
 | |
|     fn from_reflect(reflect: &dyn PartialReflect) -> Option<Self>;
 | |
| 
 | |
|     /// Attempts to downcast the given value to `Self` using,
 | |
|     /// constructing the value using [`from_reflect`] if that fails.
 | |
|     ///
 | |
|     /// This method is more efficient than using [`from_reflect`] for cases where
 | |
|     /// the given value is likely a boxed instance of `Self` (i.e. `Box<Self>`)
 | |
|     /// rather than a boxed dynamic type (e.g. [`DynamicStruct`], [`DynamicList`], etc.).
 | |
|     ///
 | |
|     /// [`from_reflect`]: Self::from_reflect
 | |
|     /// [`DynamicStruct`]: crate::DynamicStruct
 | |
|     /// [`DynamicList`]: crate::DynamicList
 | |
|     fn take_from_reflect(
 | |
|         reflect: Box<dyn PartialReflect>,
 | |
|     ) -> Result<Self, Box<dyn PartialReflect>> {
 | |
|         match reflect.try_take::<Self>() {
 | |
|             Ok(value) => Ok(value),
 | |
|             Err(value) => match Self::from_reflect(value.as_ref()) {
 | |
|                 None => Err(value),
 | |
|                 Some(value) => Ok(value),
 | |
|             },
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Type data that represents the [`FromReflect`] trait and allows it to be used dynamically.
 | |
| ///
 | |
| /// `FromReflect` allows dynamic types (e.g. [`DynamicStruct`], [`DynamicEnum`], etc.) to be converted
 | |
| /// to their full, concrete types. This is most important when it comes to deserialization where it isn't
 | |
| /// guaranteed that every field exists when trying to construct the final output.
 | |
| ///
 | |
| /// However, to do this, you normally need to specify the exact concrete type:
 | |
| ///
 | |
| /// ```
 | |
| /// # use bevy_reflect::{DynamicTupleStruct, FromReflect, Reflect};
 | |
| /// #[derive(Reflect, PartialEq, Eq, Debug)]
 | |
| /// struct Foo(#[reflect(default = "default_value")] usize);
 | |
| ///
 | |
| /// fn default_value() -> usize { 123 }
 | |
| ///
 | |
| /// let reflected = DynamicTupleStruct::default();
 | |
| ///
 | |
| /// let concrete: Foo = <Foo as FromReflect>::from_reflect(&reflected).unwrap();
 | |
| ///
 | |
| /// assert_eq!(Foo(123), concrete);
 | |
| /// ```
 | |
| ///
 | |
| /// In a dynamic context where the type might not be known at compile-time, this is nearly impossible to do.
 | |
| /// That is why this type data struct exists— it allows us to construct the full type without knowing
 | |
| /// what the actual type is.
 | |
| ///
 | |
| /// # Example
 | |
| ///
 | |
| /// ```
 | |
| /// # use bevy_reflect::{DynamicTupleStruct, Reflect, ReflectFromReflect, Typed, TypeRegistry, TypePath};
 | |
| /// # #[derive(Reflect, PartialEq, Eq, Debug)]
 | |
| /// # struct Foo(#[reflect(default = "default_value")] usize);
 | |
| /// # fn default_value() -> usize { 123 }
 | |
| /// # let mut registry = TypeRegistry::new();
 | |
| /// # registry.register::<Foo>();
 | |
| ///
 | |
| /// let mut reflected = DynamicTupleStruct::default();
 | |
| /// reflected.set_represented_type(Some(<Foo as Typed>::type_info()));
 | |
| ///
 | |
| /// let registration = registry.get_with_type_path(<Foo as TypePath>::type_path()).unwrap();
 | |
| /// let rfr = registration.data::<ReflectFromReflect>().unwrap();
 | |
| ///
 | |
| /// let concrete: Box<dyn Reflect> = rfr.from_reflect(&reflected).unwrap();
 | |
| ///
 | |
| /// assert_eq!(Foo(123), concrete.take::<Foo>().unwrap());
 | |
| /// ```
 | |
| ///
 | |
| /// [`DynamicStruct`]: crate::DynamicStruct
 | |
| /// [`DynamicEnum`]: crate::DynamicEnum
 | |
| #[derive(Clone)]
 | |
| pub struct ReflectFromReflect {
 | |
|     from_reflect: fn(&dyn PartialReflect) -> Option<Box<dyn Reflect>>,
 | |
| }
 | |
| 
 | |
| impl ReflectFromReflect {
 | |
|     /// Perform a [`FromReflect::from_reflect`] conversion on the given reflection object.
 | |
|     ///
 | |
|     /// This will convert the object to a concrete type if it wasn't already, and return
 | |
|     /// the value as `Box<dyn Reflect>`.
 | |
|     #[allow(clippy::wrong_self_convention)]
 | |
|     pub fn from_reflect(&self, reflect_value: &dyn PartialReflect) -> Option<Box<dyn Reflect>> {
 | |
|         (self.from_reflect)(reflect_value)
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl<T: FromReflect> FromType<T> for ReflectFromReflect {
 | |
|     fn from_type() -> Self {
 | |
|         Self {
 | |
|             from_reflect: |reflect_value| {
 | |
|                 T::from_reflect(reflect_value).map(|value| Box::new(value) as Box<dyn Reflect>)
 | |
|             },
 | |
|         }
 | |
|     }
 | |
| }
 |