Skip to content

Commit 1b57bf4

Browse files
MrGVSVItsDoot
authored andcommitted
bevy_reflect: Add ReflectFromReflect (v2) (bevyengine#6245)
# Objective Resolves bevyengine#4597 (based on the work from bevyengine#6056 and a refresh of bevyengine#4147) When using reflection, we may often end up in a scenario where we have a Dynamic representing a certain type. Unfortunately, we can't just call `MyType::from_reflect` as we do not have knowledge of the concrete type (`MyType`) at runtime. Such scenarios happen when we call `Reflect::clone_value`, use the reflection deserializers, or create the Dynamic type ourselves. ## Solution Add a `ReflectFromReflect` type data struct. This struct allows us to easily convert Dynamic representations of our types into their respective concrete instances. ```rust #[derive(Reflect, FromReflect)] #[reflect(FromReflect)] // <- Register `ReflectFromReflect` struct MyStruct(String); let type_id = TypeId::of::<MyStruct>(); // Register our type let mut registry = TypeRegistry::default(); registry.register::<MyStruct>(); // Create a concrete instance let my_struct = MyStruct("Hello world".to_string()); // `Reflect::clone_value` will generate a `DynamicTupleStruct` for tuple struct types let dynamic_value: Box<dyn Reflect> = my_struct.clone_value(); assert!(!dynamic_value.is::<MyStruct>()); // Get the `ReflectFromReflect` type data from the registry let rfr: &ReflectFromReflect = registry .get_type_data::<ReflectFromReflect>(type_id) .unwrap(); // Call `FromReflect::from_reflect` on our Dynamic value let concrete_value: Box<dyn Reflect> = rfr.from_reflect(&dynamic_value); assert!(concrete_value.is::<MyStruct>()); ``` ### Why this PR? ###### Why now? The three main reasons I closed bevyengine#4147 were that: 1. Registering `ReflectFromReflect` is clunky (deriving `FromReflect` *and* registering `ReflectFromReflect`) 2. The ecosystem and Bevy itself didn't seem to pay much attention to deriving `FromReflect` 3. I didn't see a lot of desire from the community for such a feature However, as time has passed it seems 2 and 3 are not really true anymore. Bevy is internally adding lots more `FromReflect` derives, which should make this feature all the more useful. Additionally, I have seen a growing number of people look for something like `ReflectFromReflect`. I think 1 is still an issue, but not a horrible one. Plus it could be made much, much better using bevyengine#6056. And I think splitting this feature out of bevyengine#6056 could lead to bevyengine#6056 being adopted sooner (or at least make the need more clear to users). ###### Why not just re-open bevyengine#4147? The main reason is so that this PR can garner more attention than simply re-opening the old one. This helps bring fresh eyes to the PR for potentially more perspectives/reviews. --- ## Changelog * Added `ReflectFromReflect` Co-authored-by: Gino Valente <[email protected]>
1 parent 5129787 commit 1b57bf4

File tree

3 files changed

+133
-15
lines changed

3 files changed

+133
-15
lines changed
+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use crate::{FromType, Reflect};
2+
3+
/// A trait for types which can be constructed from a reflected type.
4+
///
5+
/// This trait can be derived on types which implement [`Reflect`]. Some complex
6+
/// types (such as `Vec<T>`) may only be reflected if their element types
7+
/// implement this trait.
8+
///
9+
/// For structs and tuple structs, fields marked with the `#[reflect(ignore)]`
10+
/// attribute will be constructed using the `Default` implementation of the
11+
/// field type, rather than the corresponding field value (if any) of the
12+
/// reflected value.
13+
pub trait FromReflect: Reflect + Sized {
14+
/// Constructs a concrete instance of `Self` from a reflected value.
15+
fn from_reflect(reflect: &dyn Reflect) -> Option<Self>;
16+
}
17+
18+
/// Type data that represents the [`FromReflect`] trait and allows it to be used dynamically.
19+
///
20+
/// `FromReflect` allows dynamic types (e.g. [`DynamicStruct`], [`DynamicEnum`], etc.) to be converted
21+
/// to their full, concrete types. This is most important when it comes to deserialization where it isn't
22+
/// guaranteed that every field exists when trying to construct the final output.
23+
///
24+
/// However, to do this, you normally need to specify the exact concrete type:
25+
///
26+
/// ```
27+
/// # use bevy_reflect::{DynamicTupleStruct, FromReflect, Reflect};
28+
/// #[derive(Reflect, FromReflect, PartialEq, Eq, Debug)]
29+
/// struct Foo(#[reflect(default = "default_value")] usize);
30+
///
31+
/// fn default_value() -> usize { 123 }
32+
///
33+
/// let reflected = DynamicTupleStruct::default();
34+
///
35+
/// let concrete: Foo = <Foo as FromReflect>::from_reflect(&reflected).unwrap();
36+
///
37+
/// assert_eq!(Foo(123), concrete);
38+
/// ```
39+
///
40+
/// In a dynamic context where the type might not be known at compile-time, this is nearly impossible to do.
41+
/// That is why this type data struct exists— it allows us to construct the full type without knowing
42+
/// what the actual type is.
43+
///
44+
/// # Example
45+
///
46+
/// ```
47+
/// # use bevy_reflect::{DynamicTupleStruct, FromReflect, Reflect, ReflectFromReflect, TypeRegistry};
48+
/// # #[derive(Reflect, FromReflect, PartialEq, Eq, Debug)]
49+
/// # #[reflect(FromReflect)]
50+
/// # struct Foo(#[reflect(default = "default_value")] usize);
51+
/// # fn default_value() -> usize { 123 }
52+
/// # let mut registry = TypeRegistry::new();
53+
/// # registry.register::<Foo>();
54+
///
55+
/// let mut reflected = DynamicTupleStruct::default();
56+
/// reflected.set_name(std::any::type_name::<Foo>().to_string());
57+
///
58+
/// let registration = registry.get_with_name(reflected.type_name()).unwrap();
59+
/// let rfr = registration.data::<ReflectFromReflect>().unwrap();
60+
///
61+
/// let concrete: Box<dyn Reflect> = rfr.from_reflect(&reflected).unwrap();
62+
///
63+
/// assert_eq!(Foo(123), concrete.take::<Foo>().unwrap());
64+
/// ```
65+
///
66+
/// [`DynamicStruct`]: crate::DynamicStruct
67+
/// [`DynamicEnum`]: crate::DynamicEnum
68+
#[derive(Clone)]
69+
pub struct ReflectFromReflect {
70+
from_reflect: fn(&dyn Reflect) -> Option<Box<dyn Reflect>>,
71+
}
72+
73+
impl ReflectFromReflect {
74+
/// Perform a [`FromReflect::from_reflect`] conversion on the given reflection object.
75+
///
76+
/// This will convert the object to a concrete type if it wasn't already, and return
77+
/// the value as `Box<dyn Reflect>`.
78+
#[allow(clippy::wrong_self_convention)]
79+
pub fn from_reflect(&self, reflect_value: &dyn Reflect) -> Option<Box<dyn Reflect>> {
80+
(self.from_reflect)(reflect_value)
81+
}
82+
}
83+
84+
impl<T: FromReflect> FromType<T> for ReflectFromReflect {
85+
fn from_type() -> Self {
86+
Self {
87+
from_reflect: |reflect_value| {
88+
T::from_reflect(reflect_value).map(|value| Box::new(value) as Box<dyn Reflect>)
89+
},
90+
}
91+
}
92+
}

crates/bevy_reflect/src/lib.rs

+39
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
mod array;
44
mod fields;
5+
mod from_reflect;
56
mod list;
67
mod map;
78
mod path;
@@ -47,6 +48,7 @@ pub mod prelude {
4748
pub use array::*;
4849
pub use enums::*;
4950
pub use fields::*;
51+
pub use from_reflect::*;
5052
pub use impls::*;
5153
pub use list::*;
5254
pub use map::*;
@@ -103,6 +105,7 @@ mod tests {
103105
ser::{to_string_pretty, PrettyConfig},
104106
Deserializer,
105107
};
108+
use std::any::TypeId;
106109
use std::fmt::{Debug, Formatter};
107110

108111
use super::prelude::*;
@@ -244,6 +247,42 @@ mod tests {
244247
assert_eq!(values, vec![1]);
245248
}
246249

250+
#[test]
251+
fn should_call_from_reflect_dynamically() {
252+
#[derive(Reflect, FromReflect)]
253+
#[reflect(FromReflect)]
254+
struct MyStruct {
255+
foo: usize,
256+
}
257+
258+
// Register
259+
let mut registry = TypeRegistry::default();
260+
registry.register::<MyStruct>();
261+
262+
// Get type data
263+
let type_id = TypeId::of::<MyStruct>();
264+
let rfr = registry
265+
.get_type_data::<ReflectFromReflect>(type_id)
266+
.expect("the FromReflect trait should be registered");
267+
268+
// Call from_reflect
269+
let mut dynamic_struct = DynamicStruct::default();
270+
dynamic_struct.insert("foo", 123usize);
271+
let reflected = rfr
272+
.from_reflect(&dynamic_struct)
273+
.expect("the type should be properly reflected");
274+
275+
// Assert
276+
let expected = MyStruct { foo: 123 };
277+
assert!(expected
278+
.reflect_partial_eq(reflected.as_ref())
279+
.unwrap_or_default());
280+
let not_expected = MyStruct { foo: 321 };
281+
assert!(!not_expected
282+
.reflect_partial_eq(reflected.as_ref())
283+
.unwrap_or_default());
284+
}
285+
247286
#[test]
248287
fn from_reflect_should_use_default_field_attributes() {
249288
#[derive(Reflect, FromReflect, Eq, PartialEq, Debug)]

crates/bevy_reflect/src/reflect.rs

+2-15
Original file line numberDiff line numberDiff line change
@@ -215,21 +215,6 @@ pub trait Reflect: Any + Send + Sync {
215215
}
216216
}
217217

218-
/// A trait for types which can be constructed from a reflected type.
219-
///
220-
/// This trait can be derived on types which implement [`Reflect`]. Some complex
221-
/// types (such as `Vec<T>`) may only be reflected if their element types
222-
/// implement this trait.
223-
///
224-
/// For structs and tuple structs, fields marked with the `#[reflect(ignore)]`
225-
/// attribute will be constructed using the `Default` implementation of the
226-
/// field type, rather than the corresponding field value (if any) of the
227-
/// reflected value.
228-
pub trait FromReflect: Reflect + Sized {
229-
/// Constructs a concrete instance of `Self` from a reflected value.
230-
fn from_reflect(reflect: &dyn Reflect) -> Option<Self>;
231-
}
232-
233218
impl Debug for dyn Reflect {
234219
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235220
self.debug(f)
@@ -280,6 +265,8 @@ impl dyn Reflect {
280265
/// a different type, like the Dynamic\*\*\* types do, you can call `represents`
281266
/// to determine what type they represent. Represented types cannot be downcasted
282267
/// to, but you can use [`FromReflect`] to create a value of the represented type from them.
268+
///
269+
/// [`FromReflect`]: crate::FromReflect
283270
#[inline]
284271
pub fn is<T: Reflect>(&self) -> bool {
285272
self.type_id() == TypeId::of::<T>()

0 commit comments

Comments
 (0)