bevy/crates/bevy_reflect/src/impls/std.rs

1359 lines
39 KiB
Rust
Raw Normal View History

use crate::std_traits::ReflectDefault;
use crate::{self as bevy_reflect, ReflectFromPtr, ReflectOwned};
2020-11-28 00:39:59 +00:00
use crate::{
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicEnum, DynamicMap, Enum,
EnumInfo, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter,
Reflect, ReflectDeserialize, ReflectMut, ReflectRef, ReflectSerialize, TupleVariantInfo,
TypeInfo, TypeRegistration, Typed, UnitVariantInfo, UnnamedField, ValueInfo, VariantFieldIter,
VariantInfo, VariantType,
2020-11-28 00:39:59 +00:00
};
use crate::utility::{reflect_hasher, GenericTypeInfoCell, NonGenericTypeInfoCell};
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value};
use bevy_utils::{Duration, Instant};
use bevy_utils::{HashMap, HashSet};
use std::{
any::Any,
borrow::Cow,
collections::VecDeque,
ffi::OsString,
hash::{Hash, Hasher},
num::{
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
},
ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive},
path::{Path, PathBuf},
};
2020-11-28 00:39:59 +00:00
impl_reflect_value!(bool(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize,
Default
));
impl_reflect_value!(char(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize,
Default
));
impl_reflect_value!(u8(Debug, Hash, PartialEq, Serialize, Deserialize, Default));
impl_reflect_value!(u16(Debug, Hash, PartialEq, Serialize, Deserialize, Default));
impl_reflect_value!(u32(Debug, Hash, PartialEq, Serialize, Deserialize, Default));
impl_reflect_value!(u64(Debug, Hash, PartialEq, Serialize, Deserialize, Default));
impl_reflect_value!(u128(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize,
Default
));
impl_reflect_value!(usize(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize,
Default
));
impl_reflect_value!(i8(Debug, Hash, PartialEq, Serialize, Deserialize, Default));
impl_reflect_value!(i16(Debug, Hash, PartialEq, Serialize, Deserialize, Default));
impl_reflect_value!(i32(Debug, Hash, PartialEq, Serialize, Deserialize, Default));
impl_reflect_value!(i64(Debug, Hash, PartialEq, Serialize, Deserialize, Default));
impl_reflect_value!(i128(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize,
Default
));
impl_reflect_value!(isize(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize,
Default
));
impl_reflect_value!(f32(Debug, PartialEq, Serialize, Deserialize, Default));
impl_reflect_value!(f64(Debug, PartialEq, Serialize, Deserialize, Default));
impl_reflect_value!(String(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize,
Default
));
impl_reflect_value!(PathBuf(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize,
Default
));
remove blanket `Serialize + Deserialize` requirement for `Reflect` on generic types (#5197) # Objective Some generic types like `Option<T>`, `Vec<T>` and `HashMap<K, V>` implement `Reflect` when where their generic types `T`/`K`/`V` implement `Serialize + for<'de> Deserialize<'de>`. This is so that in their `GetTypeRegistration` impl they can insert the `ReflectSerialize` and `ReflectDeserialize` type data structs. This has the annoying side effect that if your struct contains a `Option<NonSerdeStruct>` you won't be able to derive reflect (https://github.com/bevyengine/bevy/issues/4054). ## Solution - remove the `Serialize + Deserialize` bounds on wrapper types - this means that `ReflectSerialize` and `ReflectDeserialize` will no longer be inserted even for `.register::<Option<DoesImplSerde>>()` - add `register_type_data<T, D>` shorthand for `registry.get_mut(T).insert(D::from_type<T>())` - require users to register their specific generic types **and the serde types** separately like ```rust .register_type::<Option<String>>() .register_type_data::<Option<String>, ReflectSerialize>() .register_type_data::<Option<String>, ReflectDeserialize>() ``` I believe this is the best we can do for extensibility and convenience without specialization. ## Changelog - `.register_type` for generic types like `Option<T>`, `Vec<T>`, `HashMap<K, V>` will no longer insert `ReflectSerialize` and `ReflectDeserialize` type data. Instead you need to register it separately for concrete generic types like so: ```rust .register_type::<Option<String>>() .register_type_data::<Option<String>, ReflectSerialize>() .register_type_data::<Option<String>, ReflectDeserialize>() ``` TODO: more docs and tweaks to the scene example to demonstrate registering generic types.
2022-07-21 14:57:37 +00:00
impl_reflect_value!(Result<T: Clone + Reflect + 'static, E: Clone + Reflect + 'static>());
impl_reflect_value!(HashSet<T: Hash + Eq + Clone + Send + Sync + 'static>());
impl_reflect_value!(Range<T: Clone + Send + Sync + 'static>());
impl_reflect_value!(RangeInclusive<T: Clone + Send + Sync + 'static>());
impl_reflect_value!(RangeFrom<T: Clone + Send + Sync + 'static>());
impl_reflect_value!(RangeTo<T: Clone + Send + Sync + 'static>());
impl_reflect_value!(RangeToInclusive<T: Clone + Send + Sync + 'static>());
impl_reflect_value!(RangeFull());
impl_reflect_value!(Duration(
Debug,
Hash,
PartialEq,
Serialize,
Deserialize,
Default
));
remove blanket `Serialize + Deserialize` requirement for `Reflect` on generic types (#5197) # Objective Some generic types like `Option<T>`, `Vec<T>` and `HashMap<K, V>` implement `Reflect` when where their generic types `T`/`K`/`V` implement `Serialize + for<'de> Deserialize<'de>`. This is so that in their `GetTypeRegistration` impl they can insert the `ReflectSerialize` and `ReflectDeserialize` type data structs. This has the annoying side effect that if your struct contains a `Option<NonSerdeStruct>` you won't be able to derive reflect (https://github.com/bevyengine/bevy/issues/4054). ## Solution - remove the `Serialize + Deserialize` bounds on wrapper types - this means that `ReflectSerialize` and `ReflectDeserialize` will no longer be inserted even for `.register::<Option<DoesImplSerde>>()` - add `register_type_data<T, D>` shorthand for `registry.get_mut(T).insert(D::from_type<T>())` - require users to register their specific generic types **and the serde types** separately like ```rust .register_type::<Option<String>>() .register_type_data::<Option<String>, ReflectSerialize>() .register_type_data::<Option<String>, ReflectDeserialize>() ``` I believe this is the best we can do for extensibility and convenience without specialization. ## Changelog - `.register_type` for generic types like `Option<T>`, `Vec<T>`, `HashMap<K, V>` will no longer insert `ReflectSerialize` and `ReflectDeserialize` type data. Instead you need to register it separately for concrete generic types like so: ```rust .register_type::<Option<String>>() .register_type_data::<Option<String>, ReflectSerialize>() .register_type_data::<Option<String>, ReflectDeserialize>() ``` TODO: more docs and tweaks to the scene example to demonstrate registering generic types.
2022-07-21 14:57:37 +00:00
impl_reflect_value!(Instant(Debug, Hash, PartialEq));
impl_reflect_value!(NonZeroI128(Debug, Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(NonZeroU128(Debug, Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(NonZeroIsize(Debug, Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(NonZeroUsize(Debug, Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(NonZeroI64(Debug, Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(NonZeroU64(Debug, Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(NonZeroU32(Debug, Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(NonZeroI32(Debug, Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(NonZeroI16(Debug, Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(NonZeroU16(Debug, Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(NonZeroU8(Debug, Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(NonZeroI8(Debug, Hash, PartialEq, Serialize, Deserialize));
2020-11-28 00:39:59 +00:00
// `Serialize` and `Deserialize` only for platforms supported by serde:
// https://github.com/serde-rs/serde/blob/3ffb86fc70efd3d329519e2dddfa306cc04f167c/serde/src/de/impls.rs#L1732
#[cfg(any(unix, windows))]
impl_reflect_value!(OsString(Debug, Hash, PartialEq, Serialize, Deserialize));
#[cfg(not(any(unix, windows)))]
impl_reflect_value!(OsString(Debug, Hash, PartialEq));
impl_from_reflect_value!(bool);
impl_from_reflect_value!(char);
impl_from_reflect_value!(u8);
impl_from_reflect_value!(u16);
impl_from_reflect_value!(u32);
impl_from_reflect_value!(u64);
impl_from_reflect_value!(u128);
impl_from_reflect_value!(usize);
impl_from_reflect_value!(i8);
impl_from_reflect_value!(i16);
impl_from_reflect_value!(i32);
impl_from_reflect_value!(i64);
impl_from_reflect_value!(i128);
impl_from_reflect_value!(isize);
impl_from_reflect_value!(f32);
impl_from_reflect_value!(f64);
impl_from_reflect_value!(String);
impl_from_reflect_value!(PathBuf);
impl_from_reflect_value!(OsString);
remove blanket `Serialize + Deserialize` requirement for `Reflect` on generic types (#5197) # Objective Some generic types like `Option<T>`, `Vec<T>` and `HashMap<K, V>` implement `Reflect` when where their generic types `T`/`K`/`V` implement `Serialize + for<'de> Deserialize<'de>`. This is so that in their `GetTypeRegistration` impl they can insert the `ReflectSerialize` and `ReflectDeserialize` type data structs. This has the annoying side effect that if your struct contains a `Option<NonSerdeStruct>` you won't be able to derive reflect (https://github.com/bevyengine/bevy/issues/4054). ## Solution - remove the `Serialize + Deserialize` bounds on wrapper types - this means that `ReflectSerialize` and `ReflectDeserialize` will no longer be inserted even for `.register::<Option<DoesImplSerde>>()` - add `register_type_data<T, D>` shorthand for `registry.get_mut(T).insert(D::from_type<T>())` - require users to register their specific generic types **and the serde types** separately like ```rust .register_type::<Option<String>>() .register_type_data::<Option<String>, ReflectSerialize>() .register_type_data::<Option<String>, ReflectDeserialize>() ``` I believe this is the best we can do for extensibility and convenience without specialization. ## Changelog - `.register_type` for generic types like `Option<T>`, `Vec<T>`, `HashMap<K, V>` will no longer insert `ReflectSerialize` and `ReflectDeserialize` type data. Instead you need to register it separately for concrete generic types like so: ```rust .register_type::<Option<String>>() .register_type_data::<Option<String>, ReflectSerialize>() .register_type_data::<Option<String>, ReflectDeserialize>() ``` TODO: more docs and tweaks to the scene example to demonstrate registering generic types.
2022-07-21 14:57:37 +00:00
impl_from_reflect_value!(HashSet<T: Hash + Eq + Clone + Send + Sync + 'static>);
impl_from_reflect_value!(Range<T: Clone + Send + Sync + 'static>);
impl_from_reflect_value!(RangeInclusive<T: Clone + Send + Sync + 'static>);
impl_from_reflect_value!(RangeFrom<T: Clone + Send + Sync + 'static>);
impl_from_reflect_value!(RangeTo<T: Clone + Send + Sync + 'static>);
impl_from_reflect_value!(RangeToInclusive<T: Clone + Send + Sync + 'static>);
impl_from_reflect_value!(RangeFull);
impl_from_reflect_value!(Duration);
impl_from_reflect_value!(Instant);
impl_from_reflect_value!(NonZeroI128);
impl_from_reflect_value!(NonZeroU128);
impl_from_reflect_value!(NonZeroIsize);
impl_from_reflect_value!(NonZeroUsize);
impl_from_reflect_value!(NonZeroI64);
impl_from_reflect_value!(NonZeroU64);
impl_from_reflect_value!(NonZeroU32);
impl_from_reflect_value!(NonZeroI32);
impl_from_reflect_value!(NonZeroI16);
impl_from_reflect_value!(NonZeroU16);
impl_from_reflect_value!(NonZeroU8);
impl_from_reflect_value!(NonZeroI8);
macro_rules! impl_reflect_for_veclike {
($ty:ty, $insert:expr, $remove:expr, $push:expr, $pop:expr, $sub:ty) => {
bevy_reflect: Decouple `List` and `Array` traits (#7467) # Objective Resolves #7121 ## Solution Decouples `List` and `Array` by removing `Array` as a supertrait of `List`. Additionally, similar methods from `Array` have been added to `List` so that their usages can remain largely unchanged. #### Possible Alternatives ##### `Sequence` My guess for why we originally made `List` a subtrait of `Array` is that they share a lot of common operations. We could potentially move these overlapping methods to a `Sequence` (name taken from #7059) trait and make that a supertrait of both. This would allow functions to contain logic that simply operates on a sequence rather than "list vs array". However, this means that we'd need to add methods for converting to a `dyn Sequence`. It also might be confusing since we wouldn't add a `ReflectRef::Sequence` or anything like that. Is such a trait worth adding (either in this PR or a followup one)? --- ## Changelog - Removed `Array` as supertrait of `List` - Added methods to `List` that were previously provided by `Array` ## Migration Guide The `List` trait is no longer dependent on `Array`. Implementors of `List` can remove the `Array` impl and move its methods into the `List` impl (with only a couple tweaks). ```rust // BEFORE impl Array for Foo { fn get(&self, index: usize) -> Option<&dyn Reflect> {/* ... */} fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {/* ... */} fn len(&self) -> usize {/* ... */} fn is_empty(&self) -> bool {/* ... */} fn iter(&self) -> ArrayIter {/* ... */} fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {/* ... */} fn clone_dynamic(&self) -> DynamicArray {/* ... */} } impl List for Foo { fn insert(&mut self, index: usize, element: Box<dyn Reflect>) {/* ... */} fn remove(&mut self, index: usize) -> Box<dyn Reflect> {/* ... */} fn push(&mut self, value: Box<dyn Reflect>) {/* ... */} fn pop(&mut self) -> Option<Box<dyn Reflect>> {/* ... */} fn clone_dynamic(&self) -> DynamicList {/* ... */} } // AFTER impl List for Foo { fn get(&self, index: usize) -> Option<&dyn Reflect> {/* ... */} fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {/* ... */} fn insert(&mut self, index: usize, element: Box<dyn Reflect>) {/* ... */} fn remove(&mut self, index: usize) -> Box<dyn Reflect> {/* ... */} fn push(&mut self, value: Box<dyn Reflect>) {/* ... */} fn pop(&mut self) -> Option<Box<dyn Reflect>> {/* ... */} fn len(&self) -> usize {/* ... */} fn is_empty(&self) -> bool {/* ... */} fn iter(&self) -> ListIter {/* ... */} fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {/* ... */} fn clone_dynamic(&self) -> DynamicList {/* ... */} } ``` Some other small tweaks that will need to be made include: - Use `ListIter` for `List::iter` instead of `ArrayIter` (the return type from `Array::iter`) - Replace `array_hash` with `list_hash` in `Reflect::reflect_hash` for implementors of `List`
2023-02-13 21:07:53 +00:00
impl<T: FromReflect> List for $ty {
#[inline]
fn get(&self, index: usize) -> Option<&dyn Reflect> {
<$sub>::get(self, index).map(|value| value as &dyn Reflect)
}
#[inline]
fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {
<$sub>::get_mut(self, index).map(|value| value as &mut dyn Reflect)
}
fn insert(&mut self, index: usize, value: Box<dyn Reflect>) {
let value = value.take::<T>().unwrap_or_else(|value| {
T::from_reflect(&*value).unwrap_or_else(|| {
panic!(
"Attempted to insert invalid value of type {}.",
value.type_name()
)
})
});
$insert(self, index, value);
}
fn remove(&mut self, index: usize) -> Box<dyn Reflect> {
Box::new($remove(self, index))
}
fn push(&mut self, value: Box<dyn Reflect>) {
let value = T::take_from_reflect(value).unwrap_or_else(|value| {
panic!(
"Attempted to push invalid value of type {}.",
value.type_name()
)
});
$push(self, value);
}
fn pop(&mut self) -> Option<Box<dyn Reflect>> {
$pop(self).map(|value| Box::new(value) as Box<dyn Reflect>)
}
bevy_reflect: Decouple `List` and `Array` traits (#7467) # Objective Resolves #7121 ## Solution Decouples `List` and `Array` by removing `Array` as a supertrait of `List`. Additionally, similar methods from `Array` have been added to `List` so that their usages can remain largely unchanged. #### Possible Alternatives ##### `Sequence` My guess for why we originally made `List` a subtrait of `Array` is that they share a lot of common operations. We could potentially move these overlapping methods to a `Sequence` (name taken from #7059) trait and make that a supertrait of both. This would allow functions to contain logic that simply operates on a sequence rather than "list vs array". However, this means that we'd need to add methods for converting to a `dyn Sequence`. It also might be confusing since we wouldn't add a `ReflectRef::Sequence` or anything like that. Is such a trait worth adding (either in this PR or a followup one)? --- ## Changelog - Removed `Array` as supertrait of `List` - Added methods to `List` that were previously provided by `Array` ## Migration Guide The `List` trait is no longer dependent on `Array`. Implementors of `List` can remove the `Array` impl and move its methods into the `List` impl (with only a couple tweaks). ```rust // BEFORE impl Array for Foo { fn get(&self, index: usize) -> Option<&dyn Reflect> {/* ... */} fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {/* ... */} fn len(&self) -> usize {/* ... */} fn is_empty(&self) -> bool {/* ... */} fn iter(&self) -> ArrayIter {/* ... */} fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {/* ... */} fn clone_dynamic(&self) -> DynamicArray {/* ... */} } impl List for Foo { fn insert(&mut self, index: usize, element: Box<dyn Reflect>) {/* ... */} fn remove(&mut self, index: usize) -> Box<dyn Reflect> {/* ... */} fn push(&mut self, value: Box<dyn Reflect>) {/* ... */} fn pop(&mut self) -> Option<Box<dyn Reflect>> {/* ... */} fn clone_dynamic(&self) -> DynamicList {/* ... */} } // AFTER impl List for Foo { fn get(&self, index: usize) -> Option<&dyn Reflect> {/* ... */} fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {/* ... */} fn insert(&mut self, index: usize, element: Box<dyn Reflect>) {/* ... */} fn remove(&mut self, index: usize) -> Box<dyn Reflect> {/* ... */} fn push(&mut self, value: Box<dyn Reflect>) {/* ... */} fn pop(&mut self) -> Option<Box<dyn Reflect>> {/* ... */} fn len(&self) -> usize {/* ... */} fn is_empty(&self) -> bool {/* ... */} fn iter(&self) -> ListIter {/* ... */} fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {/* ... */} fn clone_dynamic(&self) -> DynamicList {/* ... */} } ``` Some other small tweaks that will need to be made include: - Use `ListIter` for `List::iter` instead of `ArrayIter` (the return type from `Array::iter`) - Replace `array_hash` with `list_hash` in `Reflect::reflect_hash` for implementors of `List`
2023-02-13 21:07:53 +00:00
#[inline]
fn len(&self) -> usize {
<$sub>::len(self)
}
#[inline]
fn iter(&self) -> $crate::ListIter {
$crate::ListIter::new(self)
}
#[inline]
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
self.into_iter()
.map(|value| Box::new(value) as Box<dyn Reflect>)
.collect()
}
}
2020-11-28 00:39:59 +00:00
impl<T: FromReflect> Reflect for $ty {
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
2020-11-28 00:39:59 +00:00
fn get_type_info(&self) -> &'static TypeInfo {
<Self as Typed>::type_info()
}
bevy_reflect: Add statically available type info for reflected types (#4042) # Objective > Resolves #4504 It can be helpful to have access to type information without requiring an instance of that type. Especially for `Reflect`, a lot of the gathered type information is known at compile-time and should not necessarily require an instance. ## Solution Created a dedicated `TypeInfo` enum to store static type information. All types that derive `Reflect` now also implement the newly created `Typed` trait: ```rust pub trait Typed: Reflect { fn type_info() -> &'static TypeInfo; } ``` > Note: This trait was made separate from `Reflect` due to `Sized` restrictions. If you only have access to a `dyn Reflect`, just call `.get_type_info()` on it. This new trait method on `Reflect` should return the same value as if you had called it statically. If all you have is a `TypeId` or type name, you can get the `TypeInfo` directly from the registry using the `TypeRegistry::get_type_info` method (assuming it was registered). ### Usage Below is an example of working with `TypeInfo`. As you can see, we don't have to generate an instance of `MyTupleStruct` in order to get this information. ```rust #[derive(Reflect)] struct MyTupleStruct(usize, i32, MyStruct); let info = MyTupleStruct::type_info(); if let TypeInfo::TupleStruct(info) = info { assert!(info.is::<MyTupleStruct>()); assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name()); assert!(info.field_at(1).unwrap().is::<i32>()); } else { panic!("Expected `TypeInfo::TupleStruct`"); } ``` ### Manual Implementations It's not recommended to manually implement `Typed` yourself, but if you must, you can use the `TypeInfoCell` to automatically create and manage the static `TypeInfo`s for you (which is very helpful for blanket/generic impls): ```rust use bevy_reflect::{Reflect, TupleStructInfo, TypeInfo, UnnamedField}; use bevy_reflect::utility::TypeInfoCell; struct Foo<T: Reflect>(T); impl<T: Reflect> Typed for Foo<T> { fn type_info() -> &'static TypeInfo { static CELL: TypeInfoCell = TypeInfoCell::generic(); CELL.get_or_insert::<Self, _>(|| { let fields = [UnnamedField::new::<T>()]; let info = TupleStructInfo::new::<Self>(&fields); TypeInfo::TupleStruct(info) }) } } ``` ## Benefits One major benefit is that this opens the door to other serialization methods. Since we can get all the type info at compile time, we can know how to properly deserialize something like: ```rust #[derive(Reflect)] struct MyType { foo: usize, bar: Vec<String> } // RON to be deserialized: ( type: "my_crate::MyType", // <- We now know how to deserialize the rest of this object value: { // "foo" is a value type matching "usize" "foo": 123, // "bar" is a list type matching "Vec<String>" with item type "String" "bar": ["a", "b", "c"] } ) ``` Not only is this more compact, but it has better compatibility (we can change the type of `"foo"` to `i32` without having to update our serialized data). Of course, serialization/deserialization strategies like this may need to be discussed and fully considered before possibly making a change. However, we will be better equipped to do that now that we can access type information right from the registry. ## Discussion Some items to discuss: 1. Duplication. There's a bit of overlap with the existing traits/structs since they require an instance of the type while the type info structs do not (for example, `Struct::field_at(&self, index: usize)` and `StructInfo::field_at(&self, index: usize)`, though only `StructInfo` is accessible without an instance object). Is this okay, or do we want to handle it in another way? 2. Should `TypeInfo::Dynamic` be removed? Since the dynamic types don't have type information available at runtime, we could consider them `TypeInfo::Value`s (or just even just `TypeInfo::Struct`). The intention with `TypeInfo::Dynamic` was to keep the distinction from these dynamic types and actual structs/values since users might incorrectly believe the methods of the dynamic type's info struct would map to some contained data (which isn't possible statically). 4. General usefulness of this change, including missing/unnecessary parts. 5. Possible changes to the scene format? (One possible issue with changing it like in the example above might be that we'd have to be careful when handling generic or trait object types.) ## Compile Tests I ran a few tests to compare compile times (as suggested [here](https://github.com/bevyengine/bevy/pull/4042#discussion_r876408143)). I toggled `Reflect` and `FromReflect` derive macros using `cfg_attr` for both this PR (aa5178e7736a6f8252e10e543e52722107649d3f) and main (c309acd4322b1c3b2089e247a2d28b938eb7b56d). <details> <summary>See More</summary> The test project included 250 of the following structs (as well as a few other structs): ```rust #[derive(Default)] #[cfg_attr(feature = "reflect", derive(Reflect))] #[cfg_attr(feature = "from_reflect", derive(FromReflect))] pub struct Big001 { inventory: Inventory, foo: usize, bar: String, baz: ItemDescriptor, items: [Item; 20], hello: Option<String>, world: HashMap<i32, String>, okay: (isize, usize, /* wesize */), nope: ((String, String), (f32, f32)), blah: Cow<'static, str>, } ``` > I don't know if the compiler can optimize all these duplicate structs away, but I think it's fine either way. We're comparing times, not finding the absolute worst-case time. I only ran each build 3 times using `cargo build --timings` (thank you @devil-ira), each of which were preceeded by a `cargo clean --package bevy_reflect_compile_test`. Here are the times I got: | Test | Test 1 | Test 2 | Test 3 | Average | | -------------------------------- | ------ | ------ | ------ | ------- | | Main | 1.7s | 3.1s | 1.9s | 2.33s | | Main + `Reflect` | 8.3s | 8.6s | 8.1s | 8.33s | | Main + `Reflect` + `FromReflect` | 11.6s | 11.8s | 13.8s | 12.4s | | PR | 3.5s | 1.8s | 1.9s | 2.4s | | PR + `Reflect` | 9.2s | 8.8s | 9.3s | 9.1s | | PR + `Reflect` + `FromReflect` | 12.9s | 12.3s | 12.5s | 12.56s | </details> --- ## Future Work Even though everything could probably be made `const`, we unfortunately can't. This is because `TypeId::of::<T>()` is not yet `const` (see https://github.com/rust-lang/rust/issues/77125). When it does get stabilized, it would probably be worth coming back and making things `const`. Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-06-09 21:18:15 +00:00
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
2020-11-28 00:39:59 +00:00
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
2020-11-28 00:39:59 +00:00
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
self
}
fn as_reflect(&self) -> &dyn Reflect {
self
}
bevy_reflect: Add `as_reflect` and `as_reflect_mut` (#4350) # Objective Trait objects that have `Reflect` as a supertrait cannot be upcast to a `dyn Reflect`. Attempting something like: ```rust trait MyTrait: Reflect { // ... } fn foo(value: &dyn MyTrait) { let reflected = value as &dyn Reflect; // Error! // ... } ``` Results in `error[E0658]: trait upcasting coercion is experimental`. The reason this is important is that a lot of `bevy_reflect` methods require a `&dyn Reflect`. This is trivial with concrete types, but if we don't know the concrete type (we only have the trait object), we can't use these methods. For example, we couldn't create a `ReflectSerializer` for the type since it expects a `&dyn Reflect` value— even though we should be able to. ## Solution Add `as_reflect` and `as_reflect_mut` to `Reflect` to allow upcasting to a `dyn Reflect`: ```rust trait MyTrait: Reflect { // ... } fn foo(value: &dyn MyTrait) { let reflected = value.as_reflect(); // ... } ``` ## Alternatives We could defer this type of logic to the crate/user. They can add these methods to their trait in the same exact way we do here. The main benefit of doing it ourselves is it makes things convenient for them (especially when using the derive macro). We could also create an `AsReflect` trait with a blanket impl over all reflected types, however, I could not get that to work for trait objects since they aren't sized. --- ## Changelog - Added trait method `Reflect::as_reflect(&self)` - Added trait method `Reflect::as_reflect_mut(&mut self)` ## Migration Guide - Manual implementors of `Reflect` will need to add implementations for the methods above (this should be pretty easy as most cases just need to return `self`)
2022-04-25 13:54:48 +00:00
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}
bevy_reflect: Add `as_reflect` and `as_reflect_mut` (#4350) # Objective Trait objects that have `Reflect` as a supertrait cannot be upcast to a `dyn Reflect`. Attempting something like: ```rust trait MyTrait: Reflect { // ... } fn foo(value: &dyn MyTrait) { let reflected = value as &dyn Reflect; // Error! // ... } ``` Results in `error[E0658]: trait upcasting coercion is experimental`. The reason this is important is that a lot of `bevy_reflect` methods require a `&dyn Reflect`. This is trivial with concrete types, but if we don't know the concrete type (we only have the trait object), we can't use these methods. For example, we couldn't create a `ReflectSerializer` for the type since it expects a `&dyn Reflect` value— even though we should be able to. ## Solution Add `as_reflect` and `as_reflect_mut` to `Reflect` to allow upcasting to a `dyn Reflect`: ```rust trait MyTrait: Reflect { // ... } fn foo(value: &dyn MyTrait) { let reflected = value.as_reflect(); // ... } ``` ## Alternatives We could defer this type of logic to the crate/user. They can add these methods to their trait in the same exact way we do here. The main benefit of doing it ourselves is it makes things convenient for them (especially when using the derive macro). We could also create an `AsReflect` trait with a blanket impl over all reflected types, however, I could not get that to work for trait objects since they aren't sized. --- ## Changelog - Added trait method `Reflect::as_reflect(&self)` - Added trait method `Reflect::as_reflect_mut(&mut self)` ## Migration Guide - Manual implementors of `Reflect` will need to add implementations for the methods above (this should be pretty easy as most cases just need to return `self`)
2022-04-25 13:54:48 +00:00
fn apply(&mut self, value: &dyn Reflect) {
crate::list_apply(self, value);
}
2020-11-28 00:39:59 +00:00
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
*self = value.take()?;
Ok(())
}
2020-11-28 00:39:59 +00:00
fn reflect_ref(&self) -> ReflectRef {
ReflectRef::List(self)
}
2020-11-28 00:39:59 +00:00
fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::List(self)
}
2020-11-28 00:39:59 +00:00
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
ReflectOwned::List(self)
}
fn clone_value(&self) -> Box<dyn Reflect> {
bevy_reflect: Decouple `List` and `Array` traits (#7467) # Objective Resolves #7121 ## Solution Decouples `List` and `Array` by removing `Array` as a supertrait of `List`. Additionally, similar methods from `Array` have been added to `List` so that their usages can remain largely unchanged. #### Possible Alternatives ##### `Sequence` My guess for why we originally made `List` a subtrait of `Array` is that they share a lot of common operations. We could potentially move these overlapping methods to a `Sequence` (name taken from #7059) trait and make that a supertrait of both. This would allow functions to contain logic that simply operates on a sequence rather than "list vs array". However, this means that we'd need to add methods for converting to a `dyn Sequence`. It also might be confusing since we wouldn't add a `ReflectRef::Sequence` or anything like that. Is such a trait worth adding (either in this PR or a followup one)? --- ## Changelog - Removed `Array` as supertrait of `List` - Added methods to `List` that were previously provided by `Array` ## Migration Guide The `List` trait is no longer dependent on `Array`. Implementors of `List` can remove the `Array` impl and move its methods into the `List` impl (with only a couple tweaks). ```rust // BEFORE impl Array for Foo { fn get(&self, index: usize) -> Option<&dyn Reflect> {/* ... */} fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {/* ... */} fn len(&self) -> usize {/* ... */} fn is_empty(&self) -> bool {/* ... */} fn iter(&self) -> ArrayIter {/* ... */} fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {/* ... */} fn clone_dynamic(&self) -> DynamicArray {/* ... */} } impl List for Foo { fn insert(&mut self, index: usize, element: Box<dyn Reflect>) {/* ... */} fn remove(&mut self, index: usize) -> Box<dyn Reflect> {/* ... */} fn push(&mut self, value: Box<dyn Reflect>) {/* ... */} fn pop(&mut self) -> Option<Box<dyn Reflect>> {/* ... */} fn clone_dynamic(&self) -> DynamicList {/* ... */} } // AFTER impl List for Foo { fn get(&self, index: usize) -> Option<&dyn Reflect> {/* ... */} fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {/* ... */} fn insert(&mut self, index: usize, element: Box<dyn Reflect>) {/* ... */} fn remove(&mut self, index: usize) -> Box<dyn Reflect> {/* ... */} fn push(&mut self, value: Box<dyn Reflect>) {/* ... */} fn pop(&mut self) -> Option<Box<dyn Reflect>> {/* ... */} fn len(&self) -> usize {/* ... */} fn is_empty(&self) -> bool {/* ... */} fn iter(&self) -> ListIter {/* ... */} fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {/* ... */} fn clone_dynamic(&self) -> DynamicList {/* ... */} } ``` Some other small tweaks that will need to be made include: - Use `ListIter` for `List::iter` instead of `ArrayIter` (the return type from `Array::iter`) - Replace `array_hash` with `list_hash` in `Reflect::reflect_hash` for implementors of `List`
2023-02-13 21:07:53 +00:00
Box::new(self.clone_dynamic())
}
2020-11-28 00:39:59 +00:00
fn reflect_hash(&self) -> Option<u64> {
bevy_reflect: Decouple `List` and `Array` traits (#7467) # Objective Resolves #7121 ## Solution Decouples `List` and `Array` by removing `Array` as a supertrait of `List`. Additionally, similar methods from `Array` have been added to `List` so that their usages can remain largely unchanged. #### Possible Alternatives ##### `Sequence` My guess for why we originally made `List` a subtrait of `Array` is that they share a lot of common operations. We could potentially move these overlapping methods to a `Sequence` (name taken from #7059) trait and make that a supertrait of both. This would allow functions to contain logic that simply operates on a sequence rather than "list vs array". However, this means that we'd need to add methods for converting to a `dyn Sequence`. It also might be confusing since we wouldn't add a `ReflectRef::Sequence` or anything like that. Is such a trait worth adding (either in this PR or a followup one)? --- ## Changelog - Removed `Array` as supertrait of `List` - Added methods to `List` that were previously provided by `Array` ## Migration Guide The `List` trait is no longer dependent on `Array`. Implementors of `List` can remove the `Array` impl and move its methods into the `List` impl (with only a couple tweaks). ```rust // BEFORE impl Array for Foo { fn get(&self, index: usize) -> Option<&dyn Reflect> {/* ... */} fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {/* ... */} fn len(&self) -> usize {/* ... */} fn is_empty(&self) -> bool {/* ... */} fn iter(&self) -> ArrayIter {/* ... */} fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {/* ... */} fn clone_dynamic(&self) -> DynamicArray {/* ... */} } impl List for Foo { fn insert(&mut self, index: usize, element: Box<dyn Reflect>) {/* ... */} fn remove(&mut self, index: usize) -> Box<dyn Reflect> {/* ... */} fn push(&mut self, value: Box<dyn Reflect>) {/* ... */} fn pop(&mut self) -> Option<Box<dyn Reflect>> {/* ... */} fn clone_dynamic(&self) -> DynamicList {/* ... */} } // AFTER impl List for Foo { fn get(&self, index: usize) -> Option<&dyn Reflect> {/* ... */} fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {/* ... */} fn insert(&mut self, index: usize, element: Box<dyn Reflect>) {/* ... */} fn remove(&mut self, index: usize) -> Box<dyn Reflect> {/* ... */} fn push(&mut self, value: Box<dyn Reflect>) {/* ... */} fn pop(&mut self) -> Option<Box<dyn Reflect>> {/* ... */} fn len(&self) -> usize {/* ... */} fn is_empty(&self) -> bool {/* ... */} fn iter(&self) -> ListIter {/* ... */} fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {/* ... */} fn clone_dynamic(&self) -> DynamicList {/* ... */} } ``` Some other small tweaks that will need to be made include: - Use `ListIter` for `List::iter` instead of `ArrayIter` (the return type from `Array::iter`) - Replace `array_hash` with `list_hash` in `Reflect::reflect_hash` for implementors of `List`
2023-02-13 21:07:53 +00:00
crate::list_hash(self)
}
2020-11-28 00:39:59 +00:00
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
crate::list_partial_eq(self, value)
}
}
2020-11-28 00:39:59 +00:00
impl<T: FromReflect> Typed for $ty {
fn type_info() -> &'static TypeInfo {
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
CELL.get_or_insert::<Self, _>(|| TypeInfo::List(ListInfo::new::<Self, T>()))
}
}
bevy_reflect: Add statically available type info for reflected types (#4042) # Objective > Resolves #4504 It can be helpful to have access to type information without requiring an instance of that type. Especially for `Reflect`, a lot of the gathered type information is known at compile-time and should not necessarily require an instance. ## Solution Created a dedicated `TypeInfo` enum to store static type information. All types that derive `Reflect` now also implement the newly created `Typed` trait: ```rust pub trait Typed: Reflect { fn type_info() -> &'static TypeInfo; } ``` > Note: This trait was made separate from `Reflect` due to `Sized` restrictions. If you only have access to a `dyn Reflect`, just call `.get_type_info()` on it. This new trait method on `Reflect` should return the same value as if you had called it statically. If all you have is a `TypeId` or type name, you can get the `TypeInfo` directly from the registry using the `TypeRegistry::get_type_info` method (assuming it was registered). ### Usage Below is an example of working with `TypeInfo`. As you can see, we don't have to generate an instance of `MyTupleStruct` in order to get this information. ```rust #[derive(Reflect)] struct MyTupleStruct(usize, i32, MyStruct); let info = MyTupleStruct::type_info(); if let TypeInfo::TupleStruct(info) = info { assert!(info.is::<MyTupleStruct>()); assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name()); assert!(info.field_at(1).unwrap().is::<i32>()); } else { panic!("Expected `TypeInfo::TupleStruct`"); } ``` ### Manual Implementations It's not recommended to manually implement `Typed` yourself, but if you must, you can use the `TypeInfoCell` to automatically create and manage the static `TypeInfo`s for you (which is very helpful for blanket/generic impls): ```rust use bevy_reflect::{Reflect, TupleStructInfo, TypeInfo, UnnamedField}; use bevy_reflect::utility::TypeInfoCell; struct Foo<T: Reflect>(T); impl<T: Reflect> Typed for Foo<T> { fn type_info() -> &'static TypeInfo { static CELL: TypeInfoCell = TypeInfoCell::generic(); CELL.get_or_insert::<Self, _>(|| { let fields = [UnnamedField::new::<T>()]; let info = TupleStructInfo::new::<Self>(&fields); TypeInfo::TupleStruct(info) }) } } ``` ## Benefits One major benefit is that this opens the door to other serialization methods. Since we can get all the type info at compile time, we can know how to properly deserialize something like: ```rust #[derive(Reflect)] struct MyType { foo: usize, bar: Vec<String> } // RON to be deserialized: ( type: "my_crate::MyType", // <- We now know how to deserialize the rest of this object value: { // "foo" is a value type matching "usize" "foo": 123, // "bar" is a list type matching "Vec<String>" with item type "String" "bar": ["a", "b", "c"] } ) ``` Not only is this more compact, but it has better compatibility (we can change the type of `"foo"` to `i32` without having to update our serialized data). Of course, serialization/deserialization strategies like this may need to be discussed and fully considered before possibly making a change. However, we will be better equipped to do that now that we can access type information right from the registry. ## Discussion Some items to discuss: 1. Duplication. There's a bit of overlap with the existing traits/structs since they require an instance of the type while the type info structs do not (for example, `Struct::field_at(&self, index: usize)` and `StructInfo::field_at(&self, index: usize)`, though only `StructInfo` is accessible without an instance object). Is this okay, or do we want to handle it in another way? 2. Should `TypeInfo::Dynamic` be removed? Since the dynamic types don't have type information available at runtime, we could consider them `TypeInfo::Value`s (or just even just `TypeInfo::Struct`). The intention with `TypeInfo::Dynamic` was to keep the distinction from these dynamic types and actual structs/values since users might incorrectly believe the methods of the dynamic type's info struct would map to some contained data (which isn't possible statically). 4. General usefulness of this change, including missing/unnecessary parts. 5. Possible changes to the scene format? (One possible issue with changing it like in the example above might be that we'd have to be careful when handling generic or trait object types.) ## Compile Tests I ran a few tests to compare compile times (as suggested [here](https://github.com/bevyengine/bevy/pull/4042#discussion_r876408143)). I toggled `Reflect` and `FromReflect` derive macros using `cfg_attr` for both this PR (aa5178e7736a6f8252e10e543e52722107649d3f) and main (c309acd4322b1c3b2089e247a2d28b938eb7b56d). <details> <summary>See More</summary> The test project included 250 of the following structs (as well as a few other structs): ```rust #[derive(Default)] #[cfg_attr(feature = "reflect", derive(Reflect))] #[cfg_attr(feature = "from_reflect", derive(FromReflect))] pub struct Big001 { inventory: Inventory, foo: usize, bar: String, baz: ItemDescriptor, items: [Item; 20], hello: Option<String>, world: HashMap<i32, String>, okay: (isize, usize, /* wesize */), nope: ((String, String), (f32, f32)), blah: Cow<'static, str>, } ``` > I don't know if the compiler can optimize all these duplicate structs away, but I think it's fine either way. We're comparing times, not finding the absolute worst-case time. I only ran each build 3 times using `cargo build --timings` (thank you @devil-ira), each of which were preceeded by a `cargo clean --package bevy_reflect_compile_test`. Here are the times I got: | Test | Test 1 | Test 2 | Test 3 | Average | | -------------------------------- | ------ | ------ | ------ | ------- | | Main | 1.7s | 3.1s | 1.9s | 2.33s | | Main + `Reflect` | 8.3s | 8.6s | 8.1s | 8.33s | | Main + `Reflect` + `FromReflect` | 11.6s | 11.8s | 13.8s | 12.4s | | PR | 3.5s | 1.8s | 1.9s | 2.4s | | PR + `Reflect` | 9.2s | 8.8s | 9.3s | 9.1s | | PR + `Reflect` + `FromReflect` | 12.9s | 12.3s | 12.5s | 12.56s | </details> --- ## Future Work Even though everything could probably be made `const`, we unfortunately can't. This is because `TypeId::of::<T>()` is not yet `const` (see https://github.com/rust-lang/rust/issues/77125). When it does get stabilized, it would probably be worth coming back and making things `const`. Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-06-09 21:18:15 +00:00
impl<T: FromReflect> GetTypeRegistration for $ty {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<$ty>();
registration.insert::<ReflectFromPtr>(FromType::<$ty>::from_type());
registration
}
}
Reflection cleanup (#1536) This is an effort to provide the correct `#[reflect_value(...)]` attributes where they are needed. Supersedes #1533 and resolves #1528. --- I am working under the following assumptions (thanks to @bjorn3 and @Davier for advice here): - Any `enum` that derives `Reflect` and one or more of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } needs a `#[reflect_value(...)]` attribute containing the same subset of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } that is present on the derive. - Same as above for `struct` and `#[reflect(...)]`, respectively. - If a `struct` is used as a component, it should also have `#[reflect(Component)]` - All reflected types should be registered in their plugins I treated the following as components (added `#[reflect(Component)]` if necessary): - `bevy_render` - `struct RenderLayers` - `bevy_transform` - `struct GlobalTransform` - `struct Parent` - `struct Transform` - `bevy_ui` - `struct Style` Not treated as components: - `bevy_math` - `struct Size<T>` - `struct Rect<T>` - Note: The updates for `Size<T>` and `Rect<T>` in `bevy::math::geometry` required using @Davier's suggestion to add `+ PartialEq` to the trait bound. I then registered the specific types used over in `bevy_ui` such as `Size<Val>`, etc. in `bevy_ui`'s plugin, since `bevy::math` does not contain a plugin. - `bevy_render` - `struct Color` - `struct PipelineSpecialization` - `struct ShaderSpecialization` - `enum PrimitiveTopology` - `enum IndexFormat` Not Addressed: - I am not searching for components in Bevy that are _not_ reflected. So if there are components that are not reflected that should be reflected, that will need to be figured out in another PR. - I only added `#[reflect(...)]` or `#[reflect_value(...)]` entries for the set of four traits { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } _if they were derived via `#[derive(...)]`_. I did not look for manual trait implementations of the same set of four, nor did I consider any traits outside the four. Are those other possibilities something that needs to be looked into?
2021-03-09 23:39:41 +00:00
impl<T: FromReflect> FromReflect for $ty {
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::List(ref_list) = reflect.reflect_ref() {
let mut new_list = Self::with_capacity(ref_list.len());
for field in ref_list.iter() {
$push(&mut new_list, T::from_reflect(field)?);
}
Some(new_list)
} else {
None
}
}
}
};
}
impl_reflect_for_veclike!(Vec<T>, Vec::insert, Vec::remove, Vec::push, Vec::pop, [T]);
impl_reflect_for_veclike!(
VecDeque<T>,
VecDeque::insert,
VecDeque::remove,
VecDeque::push_back,
VecDeque::pop_back,
VecDeque::<T>
);
impl<K: FromReflect + Eq + Hash, V: FromReflect> Map for HashMap<K, V> {
2020-11-28 00:39:59 +00:00
fn get(&self, key: &dyn Reflect) -> Option<&dyn Reflect> {
key.downcast_ref::<K>()
.and_then(|key| HashMap::get(self, key))
.map(|value| value as &dyn Reflect)
}
fn get_mut(&mut self, key: &dyn Reflect) -> Option<&mut dyn Reflect> {
key.downcast_ref::<K>()
.and_then(move |key| HashMap::get_mut(self, key))
.map(|value| value as &mut dyn Reflect)
}
fn get_at(&self, index: usize) -> Option<(&dyn Reflect, &dyn Reflect)> {
self.iter()
.nth(index)
.map(|(key, value)| (key as &dyn Reflect, value as &dyn Reflect))
}
fn len(&self) -> usize {
Self::len(self)
2020-11-28 00:39:59 +00:00
}
fn iter(&self) -> MapIter {
MapIter {
map: self,
index: 0,
}
}
bevy_reflect: Get owned fields (#5728) # Objective Sometimes it's useful to be able to retrieve all the fields of a container type so that they may be processed separately. With reflection, however, we typically only have access to references. The only alternative is to "clone" the value using `Reflect::clone_value`. This, however, returns a Dynamic type in most cases. The solution there would be to use `FromReflect` instead, but this also has a problem in that it means we need to add `FromReflect` as an additional bound. ## Solution Add a `drain` method to all container traits. This returns a `Vec<Box<dyn Reflect>>` (except for `Map` which returns `Vec<(Box<dyn Reflect>, Box<dyn Reflect>)>`). This allows us to do things a lot simpler. For example, if we finished processing a struct and just need a particular value: ```rust // === OLD === // /// May or may not return a Dynamic*** value (even if `container` wasn't a `DynamicStruct`) fn get_output(container: Box<dyn Struct>, output_index: usize) -> Box<dyn Reflect> { container.field_at(output_index).unwrap().clone_value() } // === NEW === // /// Returns _exactly_ whatever was in the given struct fn get_output(container: Box<dyn Struct>, output_index: usize) -> Box<dyn Reflect> { container.drain().remove(output_index).unwrap() } ``` ### Discussion * Is `drain` the best method name? It makes sense that it "drains" all the fields and that it consumes the container in the process, but I'm open to alternatives. --- ## Changelog * Added a `drain` method to the following traits: * `Struct` * `TupleStruct` * `Tuple` * `Array` * `List` * `Map` * `Enum`
2022-08-30 21:20:58 +00:00
fn drain(self: Box<Self>) -> Vec<(Box<dyn Reflect>, Box<dyn Reflect>)> {
self.into_iter()
.map(|(key, value)| {
(
Box::new(key) as Box<dyn Reflect>,
Box::new(value) as Box<dyn Reflect>,
)
})
.collect()
}
2020-11-28 00:39:59 +00:00
fn clone_dynamic(&self) -> DynamicMap {
let mut dynamic_map = DynamicMap::default();
dynamic_map.set_name(self.type_name().to_string());
for (k, v) in self {
2020-11-28 00:39:59 +00:00
dynamic_map.insert_boxed(k.clone_value(), v.clone_value());
}
dynamic_map
}
fn insert_boxed(
&mut self,
key: Box<dyn Reflect>,
value: Box<dyn Reflect>,
) -> Option<Box<dyn Reflect>> {
let key = K::take_from_reflect(key).unwrap_or_else(|key| {
panic!(
"Attempted to insert invalid key of type {}.",
key.type_name()
)
});
let value = V::take_from_reflect(value).unwrap_or_else(|value| {
panic!(
"Attempted to insert invalid value of type {}.",
value.type_name()
)
});
self.insert(key, value)
.map(|old_value| Box::new(old_value) as Box<dyn Reflect>)
}
fn remove(&mut self, key: &dyn Reflect) -> Option<Box<dyn Reflect>> {
let mut from_reflect = None;
key.downcast_ref::<K>()
.or_else(|| {
from_reflect = K::from_reflect(key);
from_reflect.as_ref()
})
.and_then(|key| self.remove(key))
.map(|value| Box::new(value) as Box<dyn Reflect>)
}
2020-11-28 00:39:59 +00:00
}
impl<K: FromReflect + Eq + Hash, V: FromReflect> Reflect for HashMap<K, V> {
2020-11-28 00:39:59 +00:00
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
bevy_reflect: Add statically available type info for reflected types (#4042) # Objective > Resolves #4504 It can be helpful to have access to type information without requiring an instance of that type. Especially for `Reflect`, a lot of the gathered type information is known at compile-time and should not necessarily require an instance. ## Solution Created a dedicated `TypeInfo` enum to store static type information. All types that derive `Reflect` now also implement the newly created `Typed` trait: ```rust pub trait Typed: Reflect { fn type_info() -> &'static TypeInfo; } ``` > Note: This trait was made separate from `Reflect` due to `Sized` restrictions. If you only have access to a `dyn Reflect`, just call `.get_type_info()` on it. This new trait method on `Reflect` should return the same value as if you had called it statically. If all you have is a `TypeId` or type name, you can get the `TypeInfo` directly from the registry using the `TypeRegistry::get_type_info` method (assuming it was registered). ### Usage Below is an example of working with `TypeInfo`. As you can see, we don't have to generate an instance of `MyTupleStruct` in order to get this information. ```rust #[derive(Reflect)] struct MyTupleStruct(usize, i32, MyStruct); let info = MyTupleStruct::type_info(); if let TypeInfo::TupleStruct(info) = info { assert!(info.is::<MyTupleStruct>()); assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name()); assert!(info.field_at(1).unwrap().is::<i32>()); } else { panic!("Expected `TypeInfo::TupleStruct`"); } ``` ### Manual Implementations It's not recommended to manually implement `Typed` yourself, but if you must, you can use the `TypeInfoCell` to automatically create and manage the static `TypeInfo`s for you (which is very helpful for blanket/generic impls): ```rust use bevy_reflect::{Reflect, TupleStructInfo, TypeInfo, UnnamedField}; use bevy_reflect::utility::TypeInfoCell; struct Foo<T: Reflect>(T); impl<T: Reflect> Typed for Foo<T> { fn type_info() -> &'static TypeInfo { static CELL: TypeInfoCell = TypeInfoCell::generic(); CELL.get_or_insert::<Self, _>(|| { let fields = [UnnamedField::new::<T>()]; let info = TupleStructInfo::new::<Self>(&fields); TypeInfo::TupleStruct(info) }) } } ``` ## Benefits One major benefit is that this opens the door to other serialization methods. Since we can get all the type info at compile time, we can know how to properly deserialize something like: ```rust #[derive(Reflect)] struct MyType { foo: usize, bar: Vec<String> } // RON to be deserialized: ( type: "my_crate::MyType", // <- We now know how to deserialize the rest of this object value: { // "foo" is a value type matching "usize" "foo": 123, // "bar" is a list type matching "Vec<String>" with item type "String" "bar": ["a", "b", "c"] } ) ``` Not only is this more compact, but it has better compatibility (we can change the type of `"foo"` to `i32` without having to update our serialized data). Of course, serialization/deserialization strategies like this may need to be discussed and fully considered before possibly making a change. However, we will be better equipped to do that now that we can access type information right from the registry. ## Discussion Some items to discuss: 1. Duplication. There's a bit of overlap with the existing traits/structs since they require an instance of the type while the type info structs do not (for example, `Struct::field_at(&self, index: usize)` and `StructInfo::field_at(&self, index: usize)`, though only `StructInfo` is accessible without an instance object). Is this okay, or do we want to handle it in another way? 2. Should `TypeInfo::Dynamic` be removed? Since the dynamic types don't have type information available at runtime, we could consider them `TypeInfo::Value`s (or just even just `TypeInfo::Struct`). The intention with `TypeInfo::Dynamic` was to keep the distinction from these dynamic types and actual structs/values since users might incorrectly believe the methods of the dynamic type's info struct would map to some contained data (which isn't possible statically). 4. General usefulness of this change, including missing/unnecessary parts. 5. Possible changes to the scene format? (One possible issue with changing it like in the example above might be that we'd have to be careful when handling generic or trait object types.) ## Compile Tests I ran a few tests to compare compile times (as suggested [here](https://github.com/bevyengine/bevy/pull/4042#discussion_r876408143)). I toggled `Reflect` and `FromReflect` derive macros using `cfg_attr` for both this PR (aa5178e7736a6f8252e10e543e52722107649d3f) and main (c309acd4322b1c3b2089e247a2d28b938eb7b56d). <details> <summary>See More</summary> The test project included 250 of the following structs (as well as a few other structs): ```rust #[derive(Default)] #[cfg_attr(feature = "reflect", derive(Reflect))] #[cfg_attr(feature = "from_reflect", derive(FromReflect))] pub struct Big001 { inventory: Inventory, foo: usize, bar: String, baz: ItemDescriptor, items: [Item; 20], hello: Option<String>, world: HashMap<i32, String>, okay: (isize, usize, /* wesize */), nope: ((String, String), (f32, f32)), blah: Cow<'static, str>, } ``` > I don't know if the compiler can optimize all these duplicate structs away, but I think it's fine either way. We're comparing times, not finding the absolute worst-case time. I only ran each build 3 times using `cargo build --timings` (thank you @devil-ira), each of which were preceeded by a `cargo clean --package bevy_reflect_compile_test`. Here are the times I got: | Test | Test 1 | Test 2 | Test 3 | Average | | -------------------------------- | ------ | ------ | ------ | ------- | | Main | 1.7s | 3.1s | 1.9s | 2.33s | | Main + `Reflect` | 8.3s | 8.6s | 8.1s | 8.33s | | Main + `Reflect` + `FromReflect` | 11.6s | 11.8s | 13.8s | 12.4s | | PR | 3.5s | 1.8s | 1.9s | 2.4s | | PR + `Reflect` | 9.2s | 8.8s | 9.3s | 9.1s | | PR + `Reflect` + `FromReflect` | 12.9s | 12.3s | 12.5s | 12.56s | </details> --- ## Future Work Even though everything could probably be made `const`, we unfortunately can't. This is because `TypeId::of::<T>()` is not yet `const` (see https://github.com/rust-lang/rust/issues/77125). When it does get stabilized, it would probably be worth coming back and making things `const`. Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-06-09 21:18:15 +00:00
fn get_type_info(&self) -> &'static TypeInfo {
<Self as Typed>::type_info()
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
2020-11-28 00:39:59 +00:00
self
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
2020-11-28 00:39:59 +00:00
self
}
#[inline]
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
self
}
bevy_reflect: Add `as_reflect` and `as_reflect_mut` (#4350) # Objective Trait objects that have `Reflect` as a supertrait cannot be upcast to a `dyn Reflect`. Attempting something like: ```rust trait MyTrait: Reflect { // ... } fn foo(value: &dyn MyTrait) { let reflected = value as &dyn Reflect; // Error! // ... } ``` Results in `error[E0658]: trait upcasting coercion is experimental`. The reason this is important is that a lot of `bevy_reflect` methods require a `&dyn Reflect`. This is trivial with concrete types, but if we don't know the concrete type (we only have the trait object), we can't use these methods. For example, we couldn't create a `ReflectSerializer` for the type since it expects a `&dyn Reflect` value— even though we should be able to. ## Solution Add `as_reflect` and `as_reflect_mut` to `Reflect` to allow upcasting to a `dyn Reflect`: ```rust trait MyTrait: Reflect { // ... } fn foo(value: &dyn MyTrait) { let reflected = value.as_reflect(); // ... } ``` ## Alternatives We could defer this type of logic to the crate/user. They can add these methods to their trait in the same exact way we do here. The main benefit of doing it ourselves is it makes things convenient for them (especially when using the derive macro). We could also create an `AsReflect` trait with a blanket impl over all reflected types, however, I could not get that to work for trait objects since they aren't sized. --- ## Changelog - Added trait method `Reflect::as_reflect(&self)` - Added trait method `Reflect::as_reflect_mut(&mut self)` ## Migration Guide - Manual implementors of `Reflect` will need to add implementations for the methods above (this should be pretty easy as most cases just need to return `self`)
2022-04-25 13:54:48 +00:00
fn as_reflect(&self) -> &dyn Reflect {
self
}
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}
2020-11-28 00:39:59 +00:00
fn apply(&mut self, value: &dyn Reflect) {
map_apply(self, value);
2020-11-28 00:39:59 +00:00
}
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
*self = value.take()?;
Ok(())
}
fn reflect_ref(&self) -> ReflectRef {
ReflectRef::Map(self)
}
fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::Map(self)
}
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
ReflectOwned::Map(self)
}
2020-11-28 00:39:59 +00:00
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(self.clone_dynamic())
}
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
2020-11-28 00:39:59 +00:00
map_partial_eq(self, value)
}
}
impl<K: FromReflect + Eq + Hash, V: FromReflect> Typed for HashMap<K, V> {
bevy_reflect: Add statically available type info for reflected types (#4042) # Objective > Resolves #4504 It can be helpful to have access to type information without requiring an instance of that type. Especially for `Reflect`, a lot of the gathered type information is known at compile-time and should not necessarily require an instance. ## Solution Created a dedicated `TypeInfo` enum to store static type information. All types that derive `Reflect` now also implement the newly created `Typed` trait: ```rust pub trait Typed: Reflect { fn type_info() -> &'static TypeInfo; } ``` > Note: This trait was made separate from `Reflect` due to `Sized` restrictions. If you only have access to a `dyn Reflect`, just call `.get_type_info()` on it. This new trait method on `Reflect` should return the same value as if you had called it statically. If all you have is a `TypeId` or type name, you can get the `TypeInfo` directly from the registry using the `TypeRegistry::get_type_info` method (assuming it was registered). ### Usage Below is an example of working with `TypeInfo`. As you can see, we don't have to generate an instance of `MyTupleStruct` in order to get this information. ```rust #[derive(Reflect)] struct MyTupleStruct(usize, i32, MyStruct); let info = MyTupleStruct::type_info(); if let TypeInfo::TupleStruct(info) = info { assert!(info.is::<MyTupleStruct>()); assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name()); assert!(info.field_at(1).unwrap().is::<i32>()); } else { panic!("Expected `TypeInfo::TupleStruct`"); } ``` ### Manual Implementations It's not recommended to manually implement `Typed` yourself, but if you must, you can use the `TypeInfoCell` to automatically create and manage the static `TypeInfo`s for you (which is very helpful for blanket/generic impls): ```rust use bevy_reflect::{Reflect, TupleStructInfo, TypeInfo, UnnamedField}; use bevy_reflect::utility::TypeInfoCell; struct Foo<T: Reflect>(T); impl<T: Reflect> Typed for Foo<T> { fn type_info() -> &'static TypeInfo { static CELL: TypeInfoCell = TypeInfoCell::generic(); CELL.get_or_insert::<Self, _>(|| { let fields = [UnnamedField::new::<T>()]; let info = TupleStructInfo::new::<Self>(&fields); TypeInfo::TupleStruct(info) }) } } ``` ## Benefits One major benefit is that this opens the door to other serialization methods. Since we can get all the type info at compile time, we can know how to properly deserialize something like: ```rust #[derive(Reflect)] struct MyType { foo: usize, bar: Vec<String> } // RON to be deserialized: ( type: "my_crate::MyType", // <- We now know how to deserialize the rest of this object value: { // "foo" is a value type matching "usize" "foo": 123, // "bar" is a list type matching "Vec<String>" with item type "String" "bar": ["a", "b", "c"] } ) ``` Not only is this more compact, but it has better compatibility (we can change the type of `"foo"` to `i32` without having to update our serialized data). Of course, serialization/deserialization strategies like this may need to be discussed and fully considered before possibly making a change. However, we will be better equipped to do that now that we can access type information right from the registry. ## Discussion Some items to discuss: 1. Duplication. There's a bit of overlap with the existing traits/structs since they require an instance of the type while the type info structs do not (for example, `Struct::field_at(&self, index: usize)` and `StructInfo::field_at(&self, index: usize)`, though only `StructInfo` is accessible without an instance object). Is this okay, or do we want to handle it in another way? 2. Should `TypeInfo::Dynamic` be removed? Since the dynamic types don't have type information available at runtime, we could consider them `TypeInfo::Value`s (or just even just `TypeInfo::Struct`). The intention with `TypeInfo::Dynamic` was to keep the distinction from these dynamic types and actual structs/values since users might incorrectly believe the methods of the dynamic type's info struct would map to some contained data (which isn't possible statically). 4. General usefulness of this change, including missing/unnecessary parts. 5. Possible changes to the scene format? (One possible issue with changing it like in the example above might be that we'd have to be careful when handling generic or trait object types.) ## Compile Tests I ran a few tests to compare compile times (as suggested [here](https://github.com/bevyengine/bevy/pull/4042#discussion_r876408143)). I toggled `Reflect` and `FromReflect` derive macros using `cfg_attr` for both this PR (aa5178e7736a6f8252e10e543e52722107649d3f) and main (c309acd4322b1c3b2089e247a2d28b938eb7b56d). <details> <summary>See More</summary> The test project included 250 of the following structs (as well as a few other structs): ```rust #[derive(Default)] #[cfg_attr(feature = "reflect", derive(Reflect))] #[cfg_attr(feature = "from_reflect", derive(FromReflect))] pub struct Big001 { inventory: Inventory, foo: usize, bar: String, baz: ItemDescriptor, items: [Item; 20], hello: Option<String>, world: HashMap<i32, String>, okay: (isize, usize, /* wesize */), nope: ((String, String), (f32, f32)), blah: Cow<'static, str>, } ``` > I don't know if the compiler can optimize all these duplicate structs away, but I think it's fine either way. We're comparing times, not finding the absolute worst-case time. I only ran each build 3 times using `cargo build --timings` (thank you @devil-ira), each of which were preceeded by a `cargo clean --package bevy_reflect_compile_test`. Here are the times I got: | Test | Test 1 | Test 2 | Test 3 | Average | | -------------------------------- | ------ | ------ | ------ | ------- | | Main | 1.7s | 3.1s | 1.9s | 2.33s | | Main + `Reflect` | 8.3s | 8.6s | 8.1s | 8.33s | | Main + `Reflect` + `FromReflect` | 11.6s | 11.8s | 13.8s | 12.4s | | PR | 3.5s | 1.8s | 1.9s | 2.4s | | PR + `Reflect` | 9.2s | 8.8s | 9.3s | 9.1s | | PR + `Reflect` + `FromReflect` | 12.9s | 12.3s | 12.5s | 12.56s | </details> --- ## Future Work Even though everything could probably be made `const`, we unfortunately can't. This is because `TypeId::of::<T>()` is not yet `const` (see https://github.com/rust-lang/rust/issues/77125). When it does get stabilized, it would probably be worth coming back and making things `const`. Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-06-09 21:18:15 +00:00
fn type_info() -> &'static TypeInfo {
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
CELL.get_or_insert::<Self, _>(|| TypeInfo::Map(MapInfo::new::<Self, K, V>()))
}
}
Reflection cleanup (#1536) This is an effort to provide the correct `#[reflect_value(...)]` attributes where they are needed. Supersedes #1533 and resolves #1528. --- I am working under the following assumptions (thanks to @bjorn3 and @Davier for advice here): - Any `enum` that derives `Reflect` and one or more of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } needs a `#[reflect_value(...)]` attribute containing the same subset of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } that is present on the derive. - Same as above for `struct` and `#[reflect(...)]`, respectively. - If a `struct` is used as a component, it should also have `#[reflect(Component)]` - All reflected types should be registered in their plugins I treated the following as components (added `#[reflect(Component)]` if necessary): - `bevy_render` - `struct RenderLayers` - `bevy_transform` - `struct GlobalTransform` - `struct Parent` - `struct Transform` - `bevy_ui` - `struct Style` Not treated as components: - `bevy_math` - `struct Size<T>` - `struct Rect<T>` - Note: The updates for `Size<T>` and `Rect<T>` in `bevy::math::geometry` required using @Davier's suggestion to add `+ PartialEq` to the trait bound. I then registered the specific types used over in `bevy_ui` such as `Size<Val>`, etc. in `bevy_ui`'s plugin, since `bevy::math` does not contain a plugin. - `bevy_render` - `struct Color` - `struct PipelineSpecialization` - `struct ShaderSpecialization` - `enum PrimitiveTopology` - `enum IndexFormat` Not Addressed: - I am not searching for components in Bevy that are _not_ reflected. So if there are components that are not reflected that should be reflected, that will need to be figured out in another PR. - I only added `#[reflect(...)]` or `#[reflect_value(...)]` entries for the set of four traits { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } _if they were derived via `#[derive(...)]`_. I did not look for manual trait implementations of the same set of four, nor did I consider any traits outside the four. Are those other possibilities something that needs to be looked into?
2021-03-09 23:39:41 +00:00
impl<K, V> GetTypeRegistration for HashMap<K, V>
where
K: FromReflect + Eq + Hash,
V: FromReflect,
Reflection cleanup (#1536) This is an effort to provide the correct `#[reflect_value(...)]` attributes where they are needed. Supersedes #1533 and resolves #1528. --- I am working under the following assumptions (thanks to @bjorn3 and @Davier for advice here): - Any `enum` that derives `Reflect` and one or more of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } needs a `#[reflect_value(...)]` attribute containing the same subset of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } that is present on the derive. - Same as above for `struct` and `#[reflect(...)]`, respectively. - If a `struct` is used as a component, it should also have `#[reflect(Component)]` - All reflected types should be registered in their plugins I treated the following as components (added `#[reflect(Component)]` if necessary): - `bevy_render` - `struct RenderLayers` - `bevy_transform` - `struct GlobalTransform` - `struct Parent` - `struct Transform` - `bevy_ui` - `struct Style` Not treated as components: - `bevy_math` - `struct Size<T>` - `struct Rect<T>` - Note: The updates for `Size<T>` and `Rect<T>` in `bevy::math::geometry` required using @Davier's suggestion to add `+ PartialEq` to the trait bound. I then registered the specific types used over in `bevy_ui` such as `Size<Val>`, etc. in `bevy_ui`'s plugin, since `bevy::math` does not contain a plugin. - `bevy_render` - `struct Color` - `struct PipelineSpecialization` - `struct ShaderSpecialization` - `enum PrimitiveTopology` - `enum IndexFormat` Not Addressed: - I am not searching for components in Bevy that are _not_ reflected. So if there are components that are not reflected that should be reflected, that will need to be figured out in another PR. - I only added `#[reflect(...)]` or `#[reflect_value(...)]` entries for the set of four traits { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } _if they were derived via `#[derive(...)]`_. I did not look for manual trait implementations of the same set of four, nor did I consider any traits outside the four. Are those other possibilities something that needs to be looked into?
2021-03-09 23:39:41 +00:00
{
fn get_type_registration() -> TypeRegistration {
bevy_reflect: `ReflectFromPtr` to create `&dyn Reflect` from a `*const ()` (#4475) # Objective https://github.com/bevyengine/bevy/pull/4447 adds functions that can fetch resources/components as `*const ()` ptr by providing the `ComponentId`. This alone is not enough for them to be usable safely with reflection, because there is no general way to go from the raw pointer to a `&dyn Reflect` which is the pointer + a pointer to the VTable of the `Reflect` impl. By adding a `ReflectFromPtr` type that is included in the type type registration when deriving `Reflect`, safe functions can be implemented in scripting languages that don't assume a type layout and can access the component data via reflection: ```rust #[derive(Reflect)] struct StringResource { value: String } ``` ```lua local res_id = world:resource_id_by_name("example::StringResource") local res = world:resource(res_id) print(res.value) ``` ## Solution 1. add a `ReflectFromPtr` type with a `FromType<T: Reflect>` implementation and the following methods: - ` pub unsafe fn as_reflect_ptr<'a>(&self, val: Ptr<'a>) -> &'a dyn Reflect` - ` pub unsafe fn as_reflect_ptr_mut<'a>(&self, val: PtrMut<'a>) -> &'a mud dyn Reflect` Safety requirements of the methods are that you need to check that the `ReflectFromPtr` was constructed for the correct type. 2. add that type to the `TypeRegistration` in the `GetTypeRegistration` impl generated by `#[derive(Reflect)]`. This is different to other reflected traits because it doesn't need `#[reflect(ReflectReflectFromPtr)]` which IMO should be there by default. Co-authored-by: Jakob Hellermann <hellermann@sipgate.de> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-19 23:00:34 +00:00
let mut registration = TypeRegistration::of::<HashMap<K, V>>();
registration.insert::<ReflectFromPtr>(FromType::<HashMap<K, V>>::from_type());
Reflection cleanup (#1536) This is an effort to provide the correct `#[reflect_value(...)]` attributes where they are needed. Supersedes #1533 and resolves #1528. --- I am working under the following assumptions (thanks to @bjorn3 and @Davier for advice here): - Any `enum` that derives `Reflect` and one or more of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } needs a `#[reflect_value(...)]` attribute containing the same subset of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } that is present on the derive. - Same as above for `struct` and `#[reflect(...)]`, respectively. - If a `struct` is used as a component, it should also have `#[reflect(Component)]` - All reflected types should be registered in their plugins I treated the following as components (added `#[reflect(Component)]` if necessary): - `bevy_render` - `struct RenderLayers` - `bevy_transform` - `struct GlobalTransform` - `struct Parent` - `struct Transform` - `bevy_ui` - `struct Style` Not treated as components: - `bevy_math` - `struct Size<T>` - `struct Rect<T>` - Note: The updates for `Size<T>` and `Rect<T>` in `bevy::math::geometry` required using @Davier's suggestion to add `+ PartialEq` to the trait bound. I then registered the specific types used over in `bevy_ui` such as `Size<Val>`, etc. in `bevy_ui`'s plugin, since `bevy::math` does not contain a plugin. - `bevy_render` - `struct Color` - `struct PipelineSpecialization` - `struct ShaderSpecialization` - `enum PrimitiveTopology` - `enum IndexFormat` Not Addressed: - I am not searching for components in Bevy that are _not_ reflected. So if there are components that are not reflected that should be reflected, that will need to be figured out in another PR. - I only added `#[reflect(...)]` or `#[reflect_value(...)]` entries for the set of four traits { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } _if they were derived via `#[derive(...)]`_. I did not look for manual trait implementations of the same set of four, nor did I consider any traits outside the four. Are those other possibilities something that needs to be looked into?
2021-03-09 23:39:41 +00:00
registration
}
}
impl<K: FromReflect + Eq + Hash, V: FromReflect> FromReflect for HashMap<K, V> {
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::Map(ref_map) = reflect.reflect_ref() {
let mut new_map = Self::with_capacity(ref_map.len());
for (key, value) in ref_map.iter() {
let new_key = K::from_reflect(key)?;
let new_value = V::from_reflect(value)?;
new_map.insert(new_key, new_value);
}
Some(new_map)
} else {
None
}
}
}
impl<T: Reflect, const N: usize> Array for [T; N] {
#[inline]
fn get(&self, index: usize) -> Option<&dyn Reflect> {
<[T]>::get(self, index).map(|value| value as &dyn Reflect)
}
#[inline]
fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {
<[T]>::get_mut(self, index).map(|value| value as &mut dyn Reflect)
}
#[inline]
fn len(&self) -> usize {
N
}
#[inline]
fn iter(&self) -> ArrayIter {
ArrayIter::new(self)
}
bevy_reflect: Get owned fields (#5728) # Objective Sometimes it's useful to be able to retrieve all the fields of a container type so that they may be processed separately. With reflection, however, we typically only have access to references. The only alternative is to "clone" the value using `Reflect::clone_value`. This, however, returns a Dynamic type in most cases. The solution there would be to use `FromReflect` instead, but this also has a problem in that it means we need to add `FromReflect` as an additional bound. ## Solution Add a `drain` method to all container traits. This returns a `Vec<Box<dyn Reflect>>` (except for `Map` which returns `Vec<(Box<dyn Reflect>, Box<dyn Reflect>)>`). This allows us to do things a lot simpler. For example, if we finished processing a struct and just need a particular value: ```rust // === OLD === // /// May or may not return a Dynamic*** value (even if `container` wasn't a `DynamicStruct`) fn get_output(container: Box<dyn Struct>, output_index: usize) -> Box<dyn Reflect> { container.field_at(output_index).unwrap().clone_value() } // === NEW === // /// Returns _exactly_ whatever was in the given struct fn get_output(container: Box<dyn Struct>, output_index: usize) -> Box<dyn Reflect> { container.drain().remove(output_index).unwrap() } ``` ### Discussion * Is `drain` the best method name? It makes sense that it "drains" all the fields and that it consumes the container in the process, but I'm open to alternatives. --- ## Changelog * Added a `drain` method to the following traits: * `Struct` * `TupleStruct` * `Tuple` * `Array` * `List` * `Map` * `Enum`
2022-08-30 21:20:58 +00:00
#[inline]
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
self.into_iter()
.map(|value| Box::new(value) as Box<dyn Reflect>)
.collect()
}
}
impl<T: Reflect, const N: usize> Reflect for [T; N] {
#[inline]
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
bevy_reflect: Add statically available type info for reflected types (#4042) # Objective > Resolves #4504 It can be helpful to have access to type information without requiring an instance of that type. Especially for `Reflect`, a lot of the gathered type information is known at compile-time and should not necessarily require an instance. ## Solution Created a dedicated `TypeInfo` enum to store static type information. All types that derive `Reflect` now also implement the newly created `Typed` trait: ```rust pub trait Typed: Reflect { fn type_info() -> &'static TypeInfo; } ``` > Note: This trait was made separate from `Reflect` due to `Sized` restrictions. If you only have access to a `dyn Reflect`, just call `.get_type_info()` on it. This new trait method on `Reflect` should return the same value as if you had called it statically. If all you have is a `TypeId` or type name, you can get the `TypeInfo` directly from the registry using the `TypeRegistry::get_type_info` method (assuming it was registered). ### Usage Below is an example of working with `TypeInfo`. As you can see, we don't have to generate an instance of `MyTupleStruct` in order to get this information. ```rust #[derive(Reflect)] struct MyTupleStruct(usize, i32, MyStruct); let info = MyTupleStruct::type_info(); if let TypeInfo::TupleStruct(info) = info { assert!(info.is::<MyTupleStruct>()); assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name()); assert!(info.field_at(1).unwrap().is::<i32>()); } else { panic!("Expected `TypeInfo::TupleStruct`"); } ``` ### Manual Implementations It's not recommended to manually implement `Typed` yourself, but if you must, you can use the `TypeInfoCell` to automatically create and manage the static `TypeInfo`s for you (which is very helpful for blanket/generic impls): ```rust use bevy_reflect::{Reflect, TupleStructInfo, TypeInfo, UnnamedField}; use bevy_reflect::utility::TypeInfoCell; struct Foo<T: Reflect>(T); impl<T: Reflect> Typed for Foo<T> { fn type_info() -> &'static TypeInfo { static CELL: TypeInfoCell = TypeInfoCell::generic(); CELL.get_or_insert::<Self, _>(|| { let fields = [UnnamedField::new::<T>()]; let info = TupleStructInfo::new::<Self>(&fields); TypeInfo::TupleStruct(info) }) } } ``` ## Benefits One major benefit is that this opens the door to other serialization methods. Since we can get all the type info at compile time, we can know how to properly deserialize something like: ```rust #[derive(Reflect)] struct MyType { foo: usize, bar: Vec<String> } // RON to be deserialized: ( type: "my_crate::MyType", // <- We now know how to deserialize the rest of this object value: { // "foo" is a value type matching "usize" "foo": 123, // "bar" is a list type matching "Vec<String>" with item type "String" "bar": ["a", "b", "c"] } ) ``` Not only is this more compact, but it has better compatibility (we can change the type of `"foo"` to `i32` without having to update our serialized data). Of course, serialization/deserialization strategies like this may need to be discussed and fully considered before possibly making a change. However, we will be better equipped to do that now that we can access type information right from the registry. ## Discussion Some items to discuss: 1. Duplication. There's a bit of overlap with the existing traits/structs since they require an instance of the type while the type info structs do not (for example, `Struct::field_at(&self, index: usize)` and `StructInfo::field_at(&self, index: usize)`, though only `StructInfo` is accessible without an instance object). Is this okay, or do we want to handle it in another way? 2. Should `TypeInfo::Dynamic` be removed? Since the dynamic types don't have type information available at runtime, we could consider them `TypeInfo::Value`s (or just even just `TypeInfo::Struct`). The intention with `TypeInfo::Dynamic` was to keep the distinction from these dynamic types and actual structs/values since users might incorrectly believe the methods of the dynamic type's info struct would map to some contained data (which isn't possible statically). 4. General usefulness of this change, including missing/unnecessary parts. 5. Possible changes to the scene format? (One possible issue with changing it like in the example above might be that we'd have to be careful when handling generic or trait object types.) ## Compile Tests I ran a few tests to compare compile times (as suggested [here](https://github.com/bevyengine/bevy/pull/4042#discussion_r876408143)). I toggled `Reflect` and `FromReflect` derive macros using `cfg_attr` for both this PR (aa5178e7736a6f8252e10e543e52722107649d3f) and main (c309acd4322b1c3b2089e247a2d28b938eb7b56d). <details> <summary>See More</summary> The test project included 250 of the following structs (as well as a few other structs): ```rust #[derive(Default)] #[cfg_attr(feature = "reflect", derive(Reflect))] #[cfg_attr(feature = "from_reflect", derive(FromReflect))] pub struct Big001 { inventory: Inventory, foo: usize, bar: String, baz: ItemDescriptor, items: [Item; 20], hello: Option<String>, world: HashMap<i32, String>, okay: (isize, usize, /* wesize */), nope: ((String, String), (f32, f32)), blah: Cow<'static, str>, } ``` > I don't know if the compiler can optimize all these duplicate structs away, but I think it's fine either way. We're comparing times, not finding the absolute worst-case time. I only ran each build 3 times using `cargo build --timings` (thank you @devil-ira), each of which were preceeded by a `cargo clean --package bevy_reflect_compile_test`. Here are the times I got: | Test | Test 1 | Test 2 | Test 3 | Average | | -------------------------------- | ------ | ------ | ------ | ------- | | Main | 1.7s | 3.1s | 1.9s | 2.33s | | Main + `Reflect` | 8.3s | 8.6s | 8.1s | 8.33s | | Main + `Reflect` + `FromReflect` | 11.6s | 11.8s | 13.8s | 12.4s | | PR | 3.5s | 1.8s | 1.9s | 2.4s | | PR + `Reflect` | 9.2s | 8.8s | 9.3s | 9.1s | | PR + `Reflect` + `FromReflect` | 12.9s | 12.3s | 12.5s | 12.56s | </details> --- ## Future Work Even though everything could probably be made `const`, we unfortunately can't. This is because `TypeId::of::<T>()` is not yet `const` (see https://github.com/rust-lang/rust/issues/77125). When it does get stabilized, it would probably be worth coming back and making things `const`. Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-06-09 21:18:15 +00:00
fn get_type_info(&self) -> &'static TypeInfo {
<Self as Typed>::type_info()
}
#[inline]
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
#[inline]
fn as_any(&self) -> &dyn Any {
self
}
#[inline]
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
#[inline]
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
self
}
#[inline]
fn as_reflect(&self) -> &dyn Reflect {
self
}
#[inline]
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}
#[inline]
fn apply(&mut self, value: &dyn Reflect) {
crate::array_apply(self, value);
}
#[inline]
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
*self = value.take()?;
Ok(())
}
#[inline]
fn reflect_ref(&self) -> ReflectRef {
ReflectRef::Array(self)
}
#[inline]
fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::Array(self)
}
#[inline]
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
ReflectOwned::Array(self)
}
#[inline]
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(self.clone_dynamic())
}
#[inline]
fn reflect_hash(&self) -> Option<u64> {
crate::array_hash(self)
}
#[inline]
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
crate::array_partial_eq(self, value)
}
}
impl<T: FromReflect, const N: usize> FromReflect for [T; N] {
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::Array(ref_array) = reflect.reflect_ref() {
let mut temp_vec = Vec::with_capacity(ref_array.len());
for field in ref_array.iter() {
temp_vec.push(T::from_reflect(field)?);
}
temp_vec.try_into().ok()
} else {
None
}
}
}
bevy_reflect: Add statically available type info for reflected types (#4042) # Objective > Resolves #4504 It can be helpful to have access to type information without requiring an instance of that type. Especially for `Reflect`, a lot of the gathered type information is known at compile-time and should not necessarily require an instance. ## Solution Created a dedicated `TypeInfo` enum to store static type information. All types that derive `Reflect` now also implement the newly created `Typed` trait: ```rust pub trait Typed: Reflect { fn type_info() -> &'static TypeInfo; } ``` > Note: This trait was made separate from `Reflect` due to `Sized` restrictions. If you only have access to a `dyn Reflect`, just call `.get_type_info()` on it. This new trait method on `Reflect` should return the same value as if you had called it statically. If all you have is a `TypeId` or type name, you can get the `TypeInfo` directly from the registry using the `TypeRegistry::get_type_info` method (assuming it was registered). ### Usage Below is an example of working with `TypeInfo`. As you can see, we don't have to generate an instance of `MyTupleStruct` in order to get this information. ```rust #[derive(Reflect)] struct MyTupleStruct(usize, i32, MyStruct); let info = MyTupleStruct::type_info(); if let TypeInfo::TupleStruct(info) = info { assert!(info.is::<MyTupleStruct>()); assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name()); assert!(info.field_at(1).unwrap().is::<i32>()); } else { panic!("Expected `TypeInfo::TupleStruct`"); } ``` ### Manual Implementations It's not recommended to manually implement `Typed` yourself, but if you must, you can use the `TypeInfoCell` to automatically create and manage the static `TypeInfo`s for you (which is very helpful for blanket/generic impls): ```rust use bevy_reflect::{Reflect, TupleStructInfo, TypeInfo, UnnamedField}; use bevy_reflect::utility::TypeInfoCell; struct Foo<T: Reflect>(T); impl<T: Reflect> Typed for Foo<T> { fn type_info() -> &'static TypeInfo { static CELL: TypeInfoCell = TypeInfoCell::generic(); CELL.get_or_insert::<Self, _>(|| { let fields = [UnnamedField::new::<T>()]; let info = TupleStructInfo::new::<Self>(&fields); TypeInfo::TupleStruct(info) }) } } ``` ## Benefits One major benefit is that this opens the door to other serialization methods. Since we can get all the type info at compile time, we can know how to properly deserialize something like: ```rust #[derive(Reflect)] struct MyType { foo: usize, bar: Vec<String> } // RON to be deserialized: ( type: "my_crate::MyType", // <- We now know how to deserialize the rest of this object value: { // "foo" is a value type matching "usize" "foo": 123, // "bar" is a list type matching "Vec<String>" with item type "String" "bar": ["a", "b", "c"] } ) ``` Not only is this more compact, but it has better compatibility (we can change the type of `"foo"` to `i32` without having to update our serialized data). Of course, serialization/deserialization strategies like this may need to be discussed and fully considered before possibly making a change. However, we will be better equipped to do that now that we can access type information right from the registry. ## Discussion Some items to discuss: 1. Duplication. There's a bit of overlap with the existing traits/structs since they require an instance of the type while the type info structs do not (for example, `Struct::field_at(&self, index: usize)` and `StructInfo::field_at(&self, index: usize)`, though only `StructInfo` is accessible without an instance object). Is this okay, or do we want to handle it in another way? 2. Should `TypeInfo::Dynamic` be removed? Since the dynamic types don't have type information available at runtime, we could consider them `TypeInfo::Value`s (or just even just `TypeInfo::Struct`). The intention with `TypeInfo::Dynamic` was to keep the distinction from these dynamic types and actual structs/values since users might incorrectly believe the methods of the dynamic type's info struct would map to some contained data (which isn't possible statically). 4. General usefulness of this change, including missing/unnecessary parts. 5. Possible changes to the scene format? (One possible issue with changing it like in the example above might be that we'd have to be careful when handling generic or trait object types.) ## Compile Tests I ran a few tests to compare compile times (as suggested [here](https://github.com/bevyengine/bevy/pull/4042#discussion_r876408143)). I toggled `Reflect` and `FromReflect` derive macros using `cfg_attr` for both this PR (aa5178e7736a6f8252e10e543e52722107649d3f) and main (c309acd4322b1c3b2089e247a2d28b938eb7b56d). <details> <summary>See More</summary> The test project included 250 of the following structs (as well as a few other structs): ```rust #[derive(Default)] #[cfg_attr(feature = "reflect", derive(Reflect))] #[cfg_attr(feature = "from_reflect", derive(FromReflect))] pub struct Big001 { inventory: Inventory, foo: usize, bar: String, baz: ItemDescriptor, items: [Item; 20], hello: Option<String>, world: HashMap<i32, String>, okay: (isize, usize, /* wesize */), nope: ((String, String), (f32, f32)), blah: Cow<'static, str>, } ``` > I don't know if the compiler can optimize all these duplicate structs away, but I think it's fine either way. We're comparing times, not finding the absolute worst-case time. I only ran each build 3 times using `cargo build --timings` (thank you @devil-ira), each of which were preceeded by a `cargo clean --package bevy_reflect_compile_test`. Here are the times I got: | Test | Test 1 | Test 2 | Test 3 | Average | | -------------------------------- | ------ | ------ | ------ | ------- | | Main | 1.7s | 3.1s | 1.9s | 2.33s | | Main + `Reflect` | 8.3s | 8.6s | 8.1s | 8.33s | | Main + `Reflect` + `FromReflect` | 11.6s | 11.8s | 13.8s | 12.4s | | PR | 3.5s | 1.8s | 1.9s | 2.4s | | PR + `Reflect` | 9.2s | 8.8s | 9.3s | 9.1s | | PR + `Reflect` + `FromReflect` | 12.9s | 12.3s | 12.5s | 12.56s | </details> --- ## Future Work Even though everything could probably be made `const`, we unfortunately can't. This is because `TypeId::of::<T>()` is not yet `const` (see https://github.com/rust-lang/rust/issues/77125). When it does get stabilized, it would probably be worth coming back and making things `const`. Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-06-09 21:18:15 +00:00
impl<T: Reflect, const N: usize> Typed for [T; N] {
fn type_info() -> &'static TypeInfo {
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
CELL.get_or_insert::<Self, _>(|| TypeInfo::Array(ArrayInfo::new::<Self, T>(N)))
}
}
// TODO:
// `FromType::from_type` requires `Deserialize<'de>` to be implemented for `T`.
// Currently serde only supports `Deserialize<'de>` for arrays up to size 32.
// This can be changed to use const generics once serde utilizes const generics for arrays.
// Tracking issue: https://github.com/serde-rs/serde/issues/1937
macro_rules! impl_array_get_type_registration {
($($N:expr)+) => {
$(
remove blanket `Serialize + Deserialize` requirement for `Reflect` on generic types (#5197) # Objective Some generic types like `Option<T>`, `Vec<T>` and `HashMap<K, V>` implement `Reflect` when where their generic types `T`/`K`/`V` implement `Serialize + for<'de> Deserialize<'de>`. This is so that in their `GetTypeRegistration` impl they can insert the `ReflectSerialize` and `ReflectDeserialize` type data structs. This has the annoying side effect that if your struct contains a `Option<NonSerdeStruct>` you won't be able to derive reflect (https://github.com/bevyengine/bevy/issues/4054). ## Solution - remove the `Serialize + Deserialize` bounds on wrapper types - this means that `ReflectSerialize` and `ReflectDeserialize` will no longer be inserted even for `.register::<Option<DoesImplSerde>>()` - add `register_type_data<T, D>` shorthand for `registry.get_mut(T).insert(D::from_type<T>())` - require users to register their specific generic types **and the serde types** separately like ```rust .register_type::<Option<String>>() .register_type_data::<Option<String>, ReflectSerialize>() .register_type_data::<Option<String>, ReflectDeserialize>() ``` I believe this is the best we can do for extensibility and convenience without specialization. ## Changelog - `.register_type` for generic types like `Option<T>`, `Vec<T>`, `HashMap<K, V>` will no longer insert `ReflectSerialize` and `ReflectDeserialize` type data. Instead you need to register it separately for concrete generic types like so: ```rust .register_type::<Option<String>>() .register_type_data::<Option<String>, ReflectSerialize>() .register_type_data::<Option<String>, ReflectDeserialize>() ``` TODO: more docs and tweaks to the scene example to demonstrate registering generic types.
2022-07-21 14:57:37 +00:00
impl<T: Reflect > GetTypeRegistration for [T; $N] {
fn get_type_registration() -> TypeRegistration {
remove blanket `Serialize + Deserialize` requirement for `Reflect` on generic types (#5197) # Objective Some generic types like `Option<T>`, `Vec<T>` and `HashMap<K, V>` implement `Reflect` when where their generic types `T`/`K`/`V` implement `Serialize + for<'de> Deserialize<'de>`. This is so that in their `GetTypeRegistration` impl they can insert the `ReflectSerialize` and `ReflectDeserialize` type data structs. This has the annoying side effect that if your struct contains a `Option<NonSerdeStruct>` you won't be able to derive reflect (https://github.com/bevyengine/bevy/issues/4054). ## Solution - remove the `Serialize + Deserialize` bounds on wrapper types - this means that `ReflectSerialize` and `ReflectDeserialize` will no longer be inserted even for `.register::<Option<DoesImplSerde>>()` - add `register_type_data<T, D>` shorthand for `registry.get_mut(T).insert(D::from_type<T>())` - require users to register their specific generic types **and the serde types** separately like ```rust .register_type::<Option<String>>() .register_type_data::<Option<String>, ReflectSerialize>() .register_type_data::<Option<String>, ReflectDeserialize>() ``` I believe this is the best we can do for extensibility and convenience without specialization. ## Changelog - `.register_type` for generic types like `Option<T>`, `Vec<T>`, `HashMap<K, V>` will no longer insert `ReflectSerialize` and `ReflectDeserialize` type data. Instead you need to register it separately for concrete generic types like so: ```rust .register_type::<Option<String>>() .register_type_data::<Option<String>, ReflectSerialize>() .register_type_data::<Option<String>, ReflectDeserialize>() ``` TODO: more docs and tweaks to the scene example to demonstrate registering generic types.
2022-07-21 14:57:37 +00:00
TypeRegistration::of::<[T; $N]>()
}
}
)+
};
}
impl_array_get_type_registration! {
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32
}
impl<T: FromReflect> GetTypeRegistration for Option<T> {
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
fn get_type_registration() -> TypeRegistration {
TypeRegistration::of::<Option<T>>()
}
}
impl<T: FromReflect> Enum for Option<T> {
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
fn field(&self, _name: &str) -> Option<&dyn Reflect> {
None
}
fn field_at(&self, index: usize) -> Option<&dyn Reflect> {
match self {
Some(value) if index == 0 => Some(value),
_ => None,
}
}
fn field_mut(&mut self, _name: &str) -> Option<&mut dyn Reflect> {
None
}
fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {
match self {
Some(value) if index == 0 => Some(value),
_ => None,
}
}
fn index_of(&self, _name: &str) -> Option<usize> {
None
}
fn name_at(&self, _index: usize) -> Option<&str> {
None
}
fn iter_fields(&self) -> VariantFieldIter {
VariantFieldIter::new(self)
}
#[inline]
fn field_len(&self) -> usize {
match self {
Some(..) => 1,
None => 0,
}
}
#[inline]
fn variant_name(&self) -> &str {
match self {
Some(..) => "Some",
None => "None",
}
}
bevy_reflect: Improve serialization format even more (#5723) > Note: This is rebased off #4561 and can be viewed as a competitor to that PR. See `Comparison with #4561` section for details. # Objective The current serialization format used by `bevy_reflect` is both verbose and error-prone. Taking the following structs[^1] for example: ```rust // -- src/inventory.rs #[derive(Reflect)] struct Inventory { id: String, max_storage: usize, items: Vec<Item> } #[derive(Reflect)] struct Item { name: String } ``` Given an inventory of a single item, this would serialize to something like: ```rust // -- assets/inventory.ron { "type": "my_game::inventory::Inventory", "struct": { "id": { "type": "alloc::string::String", "value": "inv001", }, "max_storage": { "type": "usize", "value": 10 }, "items": { "type": "alloc::vec::Vec<alloc::string::String>", "list": [ { "type": "my_game::inventory::Item", "struct": { "name": { "type": "alloc::string::String", "value": "Pickaxe" }, }, }, ], }, }, } ``` Aside from being really long and difficult to read, it also has a few "gotchas" that users need to be aware of if they want to edit the file manually. A major one is the requirement that you use the proper keys for a given type. For structs, you need `"struct"`. For lists, `"list"`. For tuple structs, `"tuple_struct"`. And so on. It also ***requires*** that the `"type"` entry come before the actual data. Despite being a map— which in programming is almost always orderless by default— the entries need to be in a particular order. Failure to follow the ordering convention results in a failure to deserialize the data. This makes it very prone to errors and annoyances. ## Solution Using #4042, we can remove a lot of the boilerplate and metadata needed by this older system. Since we now have static access to type information, we can simplify our serialized data to look like: ```rust // -- assets/inventory.ron { "my_game::inventory::Inventory": ( id: "inv001", max_storage: 10, items: [ ( name: "Pickaxe" ), ], ), } ``` This is much more digestible and a lot less error-prone (no more key requirements and no more extra type names). Additionally, it is a lot more familiar to users as it follows conventional serde mechanics. For example, the struct is represented with `(...)` when serialized to RON. #### Custom Serialization Additionally, this PR adds the opt-in ability to specify a custom serde implementation to be used rather than the one created via reflection. For example[^1]: ```rust // -- src/inventory.rs #[derive(Reflect, Serialize)] #[reflect(Serialize)] struct Item { #[serde(alias = "id")] name: String } ``` ```rust // -- assets/inventory.ron { "my_game::inventory::Inventory": ( id: "inv001", max_storage: 10, items: [ ( id: "Pickaxe" ), ], ), }, ``` By allowing users to define their own serialization methods, we do two things: 1. We give more control over how data is serialized/deserialized to the end user 2. We avoid having to re-define serde's attributes and forcing users to apply both (e.g. we don't need a `#[reflect(alias)]` attribute). ### Improved Formats One of the improvements this PR provides is the ability to represent data in ways that are more conventional and/or familiar to users. Many users are familiar with RON so here are some of the ways we can now represent data in RON: ###### Structs ```js { "my_crate::Foo": ( bar: 123 ) } // OR { "my_crate::Foo": Foo( bar: 123 ) } ``` <details> <summary>Old Format</summary> ```js { "type": "my_crate::Foo", "struct": { "bar": { "type": "usize", "value": 123 } } } ``` </details> ###### Tuples ```js { "(f32, f32)": (1.0, 2.0) } ``` <details> <summary>Old Format</summary> ```js { "type": "(f32, f32)", "tuple": [ { "type": "f32", "value": 1.0 }, { "type": "f32", "value": 2.0 } ] } ``` </details> ###### Tuple Structs ```js { "my_crate::Bar": ("Hello World!") } // OR { "my_crate::Bar": Bar("Hello World!") } ``` <details> <summary>Old Format</summary> ```js { "type": "my_crate::Bar", "tuple_struct": [ { "type": "alloc::string::String", "value": "Hello World!" } ] } ``` </details> ###### Arrays It may be a bit surprising to some, but arrays now also use the tuple format. This is because they essentially _are_ tuples (a sequence of values with a fixed size), but only allow for homogenous types. Additionally, this is how RON handles them and is probably a result of the 32-capacity limit imposed on them (both by [serde](https://docs.rs/serde/latest/serde/trait.Serialize.html#impl-Serialize-for-%5BT%3B%2032%5D) and by [bevy_reflect](https://docs.rs/bevy/latest/bevy/reflect/trait.GetTypeRegistration.html#impl-GetTypeRegistration-for-%5BT%3B%2032%5D)). ```js { "[i32; 3]": (1, 2, 3) } ``` <details> <summary>Old Format</summary> ```js { "type": "[i32; 3]", "array": [ { "type": "i32", "value": 1 }, { "type": "i32", "value": 2 }, { "type": "i32", "value": 3 } ] } ``` </details> ###### Enums To make things simple, I'll just put a struct variant here, but the style applies to all variant types: ```js { "my_crate::ItemType": Consumable( name: "Healing potion" ) } ``` <details> <summary>Old Format</summary> ```js { "type": "my_crate::ItemType", "enum": { "variant": "Consumable", "struct": { "name": { "type": "alloc::string::String", "value": "Healing potion" } } } } ``` </details> ### Comparison with #4561 This PR is a rebased version of #4561. The reason for the split between the two is because this PR creates a _very_ different scene format. You may notice that the PR descriptions for either PR are pretty similar. This was done to better convey the changes depending on which (if any) gets merged first. If #4561 makes it in first, I will update this PR description accordingly. --- ## Changelog * Re-worked serialization/deserialization for reflected types * Added `TypedReflectDeserializer` for deserializing data with known `TypeInfo` * Renamed `ReflectDeserializer` to `UntypedReflectDeserializer` * ~~Replaced usages of `deserialize_any` with `deserialize_map` for non-self-describing formats~~ Reverted this change since there are still some issues that need to be sorted out (in a separate PR). By reverting this, crates like `bincode` can throw an error when attempting to deserialize non-self-describing formats (`bincode` results in `DeserializeAnyNotSupported`) * Structs, tuples, tuple structs, arrays, and enums are now all de/serialized using conventional serde methods ## Migration Guide * This PR reduces the verbosity of the scene format. Scenes will need to be updated accordingly: ```js // Old format { "type": "my_game::item::Item", "struct": { "id": { "type": "alloc::string::String", "value": "bevycraft:stone", }, "tags": { "type": "alloc::vec::Vec<alloc::string::String>", "list": [ { "type": "alloc::string::String", "value": "material" }, ], }, } // New format { "my_game::item::Item": ( id: "bevycraft:stone", tags: ["material"] ) } ``` [^1]: Some derives omitted for brevity.
2022-09-20 19:38:18 +00:00
fn variant_index(&self) -> usize {
match self {
None => 0,
Some(..) => 1,
}
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
#[inline]
fn variant_type(&self) -> VariantType {
match self {
Some(..) => VariantType::Tuple,
None => VariantType::Unit,
}
}
fn clone_dynamic(&self) -> DynamicEnum {
DynamicEnum::from_ref::<Self>(self)
}
}
impl<T: FromReflect> Reflect for Option<T> {
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
#[inline]
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
#[inline]
fn get_type_info(&self) -> &'static TypeInfo {
<Self as Typed>::type_info()
}
#[inline]
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
#[inline]
fn as_any(&self) -> &dyn Any {
self
}
#[inline]
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
#[inline]
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
self
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
fn as_reflect(&self) -> &dyn Reflect {
self
}
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}
#[inline]
fn apply(&mut self, value: &dyn Reflect) {
if let ReflectRef::Enum(value) = value.reflect_ref() {
if self.variant_name() == value.variant_name() {
// Same variant -> just update fields
for (index, field) in value.iter_fields().enumerate() {
if let Some(v) = self.field_at_mut(index) {
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
v.apply(field.value());
}
}
} else {
// New variant -> perform a switch
match value.variant_name() {
"Some" => {
let field = T::take_from_reflect(
value
.field_at(0)
.unwrap_or_else(|| {
panic!(
"Field in `Some` variant of {} should exist",
std::any::type_name::<Option<T>>()
)
})
.clone_value(),
)
.unwrap_or_else(|_| {
panic!(
"Field in `Some` variant of {} should be of type {}",
std::any::type_name::<Option<T>>(),
std::any::type_name::<T>()
)
});
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
*self = Some(field);
}
"None" => {
*self = None;
}
_ => panic!("Enum is not a {}.", std::any::type_name::<Self>()),
}
}
}
}
#[inline]
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
*self = value.take()?;
Ok(())
}
fn reflect_ref(&self) -> ReflectRef {
ReflectRef::Enum(self)
}
fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::Enum(self)
}
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
ReflectOwned::Enum(self)
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
#[inline]
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(Enum::clone_dynamic(self))
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
}
fn reflect_hash(&self) -> Option<u64> {
crate::enum_hash(self)
}
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
crate::enum_partial_eq(self, value)
}
}
impl<T: FromReflect> FromReflect for Option<T> {
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::Enum(dyn_enum) = reflect.reflect_ref() {
match dyn_enum.variant_name() {
"Some" => {
let field = T::take_from_reflect(
dyn_enum
.field_at(0)
.unwrap_or_else(|| {
panic!(
"Field in `Some` variant of {} should exist",
std::any::type_name::<Option<T>>()
)
})
.clone_value(),
)
.unwrap_or_else(|_| {
panic!(
"Field in `Some` variant of {} should be of type {}",
std::any::type_name::<Option<T>>(),
std::any::type_name::<T>()
)
});
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
Some(Some(field))
}
"None" => Some(None),
name => panic!(
"variant with name `{}` does not exist on enum `{}`",
name,
std::any::type_name::<Self>()
),
}
} else {
None
}
}
}
impl<T: FromReflect> Typed for Option<T> {
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
fn type_info() -> &'static TypeInfo {
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
CELL.get_or_insert::<Self, _>(|| {
bevy_reflect: Improve serialization format even more (#5723) > Note: This is rebased off #4561 and can be viewed as a competitor to that PR. See `Comparison with #4561` section for details. # Objective The current serialization format used by `bevy_reflect` is both verbose and error-prone. Taking the following structs[^1] for example: ```rust // -- src/inventory.rs #[derive(Reflect)] struct Inventory { id: String, max_storage: usize, items: Vec<Item> } #[derive(Reflect)] struct Item { name: String } ``` Given an inventory of a single item, this would serialize to something like: ```rust // -- assets/inventory.ron { "type": "my_game::inventory::Inventory", "struct": { "id": { "type": "alloc::string::String", "value": "inv001", }, "max_storage": { "type": "usize", "value": 10 }, "items": { "type": "alloc::vec::Vec<alloc::string::String>", "list": [ { "type": "my_game::inventory::Item", "struct": { "name": { "type": "alloc::string::String", "value": "Pickaxe" }, }, }, ], }, }, } ``` Aside from being really long and difficult to read, it also has a few "gotchas" that users need to be aware of if they want to edit the file manually. A major one is the requirement that you use the proper keys for a given type. For structs, you need `"struct"`. For lists, `"list"`. For tuple structs, `"tuple_struct"`. And so on. It also ***requires*** that the `"type"` entry come before the actual data. Despite being a map— which in programming is almost always orderless by default— the entries need to be in a particular order. Failure to follow the ordering convention results in a failure to deserialize the data. This makes it very prone to errors and annoyances. ## Solution Using #4042, we can remove a lot of the boilerplate and metadata needed by this older system. Since we now have static access to type information, we can simplify our serialized data to look like: ```rust // -- assets/inventory.ron { "my_game::inventory::Inventory": ( id: "inv001", max_storage: 10, items: [ ( name: "Pickaxe" ), ], ), } ``` This is much more digestible and a lot less error-prone (no more key requirements and no more extra type names). Additionally, it is a lot more familiar to users as it follows conventional serde mechanics. For example, the struct is represented with `(...)` when serialized to RON. #### Custom Serialization Additionally, this PR adds the opt-in ability to specify a custom serde implementation to be used rather than the one created via reflection. For example[^1]: ```rust // -- src/inventory.rs #[derive(Reflect, Serialize)] #[reflect(Serialize)] struct Item { #[serde(alias = "id")] name: String } ``` ```rust // -- assets/inventory.ron { "my_game::inventory::Inventory": ( id: "inv001", max_storage: 10, items: [ ( id: "Pickaxe" ), ], ), }, ``` By allowing users to define their own serialization methods, we do two things: 1. We give more control over how data is serialized/deserialized to the end user 2. We avoid having to re-define serde's attributes and forcing users to apply both (e.g. we don't need a `#[reflect(alias)]` attribute). ### Improved Formats One of the improvements this PR provides is the ability to represent data in ways that are more conventional and/or familiar to users. Many users are familiar with RON so here are some of the ways we can now represent data in RON: ###### Structs ```js { "my_crate::Foo": ( bar: 123 ) } // OR { "my_crate::Foo": Foo( bar: 123 ) } ``` <details> <summary>Old Format</summary> ```js { "type": "my_crate::Foo", "struct": { "bar": { "type": "usize", "value": 123 } } } ``` </details> ###### Tuples ```js { "(f32, f32)": (1.0, 2.0) } ``` <details> <summary>Old Format</summary> ```js { "type": "(f32, f32)", "tuple": [ { "type": "f32", "value": 1.0 }, { "type": "f32", "value": 2.0 } ] } ``` </details> ###### Tuple Structs ```js { "my_crate::Bar": ("Hello World!") } // OR { "my_crate::Bar": Bar("Hello World!") } ``` <details> <summary>Old Format</summary> ```js { "type": "my_crate::Bar", "tuple_struct": [ { "type": "alloc::string::String", "value": "Hello World!" } ] } ``` </details> ###### Arrays It may be a bit surprising to some, but arrays now also use the tuple format. This is because they essentially _are_ tuples (a sequence of values with a fixed size), but only allow for homogenous types. Additionally, this is how RON handles them and is probably a result of the 32-capacity limit imposed on them (both by [serde](https://docs.rs/serde/latest/serde/trait.Serialize.html#impl-Serialize-for-%5BT%3B%2032%5D) and by [bevy_reflect](https://docs.rs/bevy/latest/bevy/reflect/trait.GetTypeRegistration.html#impl-GetTypeRegistration-for-%5BT%3B%2032%5D)). ```js { "[i32; 3]": (1, 2, 3) } ``` <details> <summary>Old Format</summary> ```js { "type": "[i32; 3]", "array": [ { "type": "i32", "value": 1 }, { "type": "i32", "value": 2 }, { "type": "i32", "value": 3 } ] } ``` </details> ###### Enums To make things simple, I'll just put a struct variant here, but the style applies to all variant types: ```js { "my_crate::ItemType": Consumable( name: "Healing potion" ) } ``` <details> <summary>Old Format</summary> ```js { "type": "my_crate::ItemType", "enum": { "variant": "Consumable", "struct": { "name": { "type": "alloc::string::String", "value": "Healing potion" } } } } ``` </details> ### Comparison with #4561 This PR is a rebased version of #4561. The reason for the split between the two is because this PR creates a _very_ different scene format. You may notice that the PR descriptions for either PR are pretty similar. This was done to better convey the changes depending on which (if any) gets merged first. If #4561 makes it in first, I will update this PR description accordingly. --- ## Changelog * Re-worked serialization/deserialization for reflected types * Added `TypedReflectDeserializer` for deserializing data with known `TypeInfo` * Renamed `ReflectDeserializer` to `UntypedReflectDeserializer` * ~~Replaced usages of `deserialize_any` with `deserialize_map` for non-self-describing formats~~ Reverted this change since there are still some issues that need to be sorted out (in a separate PR). By reverting this, crates like `bincode` can throw an error when attempting to deserialize non-self-describing formats (`bincode` results in `DeserializeAnyNotSupported`) * Structs, tuples, tuple structs, arrays, and enums are now all de/serialized using conventional serde methods ## Migration Guide * This PR reduces the verbosity of the scene format. Scenes will need to be updated accordingly: ```js // Old format { "type": "my_game::item::Item", "struct": { "id": { "type": "alloc::string::String", "value": "bevycraft:stone", }, "tags": { "type": "alloc::vec::Vec<alloc::string::String>", "list": [ { "type": "alloc::string::String", "value": "material" }, ], }, } // New format { "my_game::item::Item": ( id: "bevycraft:stone", tags: ["material"] ) } ``` [^1]: Some derives omitted for brevity.
2022-09-20 19:38:18 +00:00
let none_variant = VariantInfo::Unit(UnitVariantInfo::new("None"));
let some_variant =
VariantInfo::Tuple(TupleVariantInfo::new("Some", &[UnnamedField::new::<T>(0)]));
TypeInfo::Enum(EnumInfo::new::<Self>(
"Option",
&[none_variant, some_variant],
))
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
})
}
}
impl Reflect for Cow<'static, str> {
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
fn get_type_info(&self) -> &'static TypeInfo {
<Self as Typed>::type_info()
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
self
}
fn as_reflect(&self) -> &dyn Reflect {
self
}
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}
fn apply(&mut self, value: &dyn Reflect) {
let value = value.as_any();
if let Some(value) = value.downcast_ref::<Self>() {
*self = value.clone();
} else {
panic!("Value is not a {}.", std::any::type_name::<Self>());
}
}
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
*self = value.take()?;
Ok(())
}
fn reflect_ref(&self) -> ReflectRef {
ReflectRef::Value(self)
}
fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::Value(self)
}
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
ReflectOwned::Value(self)
}
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(self.clone())
}
fn reflect_hash(&self) -> Option<u64> {
let mut hasher = reflect_hasher();
Hash::hash(&std::any::Any::type_id(self), &mut hasher);
Hash::hash(self, &mut hasher);
Some(hasher.finish())
}
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
let value = value.as_any();
if let Some(value) = value.downcast_ref::<Self>() {
Some(std::cmp::PartialEq::eq(self, value))
} else {
Some(false)
}
}
}
bevy_reflect: Add statically available type info for reflected types (#4042) # Objective > Resolves #4504 It can be helpful to have access to type information without requiring an instance of that type. Especially for `Reflect`, a lot of the gathered type information is known at compile-time and should not necessarily require an instance. ## Solution Created a dedicated `TypeInfo` enum to store static type information. All types that derive `Reflect` now also implement the newly created `Typed` trait: ```rust pub trait Typed: Reflect { fn type_info() -> &'static TypeInfo; } ``` > Note: This trait was made separate from `Reflect` due to `Sized` restrictions. If you only have access to a `dyn Reflect`, just call `.get_type_info()` on it. This new trait method on `Reflect` should return the same value as if you had called it statically. If all you have is a `TypeId` or type name, you can get the `TypeInfo` directly from the registry using the `TypeRegistry::get_type_info` method (assuming it was registered). ### Usage Below is an example of working with `TypeInfo`. As you can see, we don't have to generate an instance of `MyTupleStruct` in order to get this information. ```rust #[derive(Reflect)] struct MyTupleStruct(usize, i32, MyStruct); let info = MyTupleStruct::type_info(); if let TypeInfo::TupleStruct(info) = info { assert!(info.is::<MyTupleStruct>()); assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name()); assert!(info.field_at(1).unwrap().is::<i32>()); } else { panic!("Expected `TypeInfo::TupleStruct`"); } ``` ### Manual Implementations It's not recommended to manually implement `Typed` yourself, but if you must, you can use the `TypeInfoCell` to automatically create and manage the static `TypeInfo`s for you (which is very helpful for blanket/generic impls): ```rust use bevy_reflect::{Reflect, TupleStructInfo, TypeInfo, UnnamedField}; use bevy_reflect::utility::TypeInfoCell; struct Foo<T: Reflect>(T); impl<T: Reflect> Typed for Foo<T> { fn type_info() -> &'static TypeInfo { static CELL: TypeInfoCell = TypeInfoCell::generic(); CELL.get_or_insert::<Self, _>(|| { let fields = [UnnamedField::new::<T>()]; let info = TupleStructInfo::new::<Self>(&fields); TypeInfo::TupleStruct(info) }) } } ``` ## Benefits One major benefit is that this opens the door to other serialization methods. Since we can get all the type info at compile time, we can know how to properly deserialize something like: ```rust #[derive(Reflect)] struct MyType { foo: usize, bar: Vec<String> } // RON to be deserialized: ( type: "my_crate::MyType", // <- We now know how to deserialize the rest of this object value: { // "foo" is a value type matching "usize" "foo": 123, // "bar" is a list type matching "Vec<String>" with item type "String" "bar": ["a", "b", "c"] } ) ``` Not only is this more compact, but it has better compatibility (we can change the type of `"foo"` to `i32` without having to update our serialized data). Of course, serialization/deserialization strategies like this may need to be discussed and fully considered before possibly making a change. However, we will be better equipped to do that now that we can access type information right from the registry. ## Discussion Some items to discuss: 1. Duplication. There's a bit of overlap with the existing traits/structs since they require an instance of the type while the type info structs do not (for example, `Struct::field_at(&self, index: usize)` and `StructInfo::field_at(&self, index: usize)`, though only `StructInfo` is accessible without an instance object). Is this okay, or do we want to handle it in another way? 2. Should `TypeInfo::Dynamic` be removed? Since the dynamic types don't have type information available at runtime, we could consider them `TypeInfo::Value`s (or just even just `TypeInfo::Struct`). The intention with `TypeInfo::Dynamic` was to keep the distinction from these dynamic types and actual structs/values since users might incorrectly believe the methods of the dynamic type's info struct would map to some contained data (which isn't possible statically). 4. General usefulness of this change, including missing/unnecessary parts. 5. Possible changes to the scene format? (One possible issue with changing it like in the example above might be that we'd have to be careful when handling generic or trait object types.) ## Compile Tests I ran a few tests to compare compile times (as suggested [here](https://github.com/bevyengine/bevy/pull/4042#discussion_r876408143)). I toggled `Reflect` and `FromReflect` derive macros using `cfg_attr` for both this PR (aa5178e7736a6f8252e10e543e52722107649d3f) and main (c309acd4322b1c3b2089e247a2d28b938eb7b56d). <details> <summary>See More</summary> The test project included 250 of the following structs (as well as a few other structs): ```rust #[derive(Default)] #[cfg_attr(feature = "reflect", derive(Reflect))] #[cfg_attr(feature = "from_reflect", derive(FromReflect))] pub struct Big001 { inventory: Inventory, foo: usize, bar: String, baz: ItemDescriptor, items: [Item; 20], hello: Option<String>, world: HashMap<i32, String>, okay: (isize, usize, /* wesize */), nope: ((String, String), (f32, f32)), blah: Cow<'static, str>, } ``` > I don't know if the compiler can optimize all these duplicate structs away, but I think it's fine either way. We're comparing times, not finding the absolute worst-case time. I only ran each build 3 times using `cargo build --timings` (thank you @devil-ira), each of which were preceeded by a `cargo clean --package bevy_reflect_compile_test`. Here are the times I got: | Test | Test 1 | Test 2 | Test 3 | Average | | -------------------------------- | ------ | ------ | ------ | ------- | | Main | 1.7s | 3.1s | 1.9s | 2.33s | | Main + `Reflect` | 8.3s | 8.6s | 8.1s | 8.33s | | Main + `Reflect` + `FromReflect` | 11.6s | 11.8s | 13.8s | 12.4s | | PR | 3.5s | 1.8s | 1.9s | 2.4s | | PR + `Reflect` | 9.2s | 8.8s | 9.3s | 9.1s | | PR + `Reflect` + `FromReflect` | 12.9s | 12.3s | 12.5s | 12.56s | </details> --- ## Future Work Even though everything could probably be made `const`, we unfortunately can't. This is because `TypeId::of::<T>()` is not yet `const` (see https://github.com/rust-lang/rust/issues/77125). When it does get stabilized, it would probably be worth coming back and making things `const`. Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-06-09 21:18:15 +00:00
impl Typed for Cow<'static, str> {
fn type_info() -> &'static TypeInfo {
static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
CELL.get_or_set(|| TypeInfo::Value(ValueInfo::new::<Self>()))
}
}
Reflection cleanup (#1536) This is an effort to provide the correct `#[reflect_value(...)]` attributes where they are needed. Supersedes #1533 and resolves #1528. --- I am working under the following assumptions (thanks to @bjorn3 and @Davier for advice here): - Any `enum` that derives `Reflect` and one or more of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } needs a `#[reflect_value(...)]` attribute containing the same subset of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } that is present on the derive. - Same as above for `struct` and `#[reflect(...)]`, respectively. - If a `struct` is used as a component, it should also have `#[reflect(Component)]` - All reflected types should be registered in their plugins I treated the following as components (added `#[reflect(Component)]` if necessary): - `bevy_render` - `struct RenderLayers` - `bevy_transform` - `struct GlobalTransform` - `struct Parent` - `struct Transform` - `bevy_ui` - `struct Style` Not treated as components: - `bevy_math` - `struct Size<T>` - `struct Rect<T>` - Note: The updates for `Size<T>` and `Rect<T>` in `bevy::math::geometry` required using @Davier's suggestion to add `+ PartialEq` to the trait bound. I then registered the specific types used over in `bevy_ui` such as `Size<Val>`, etc. in `bevy_ui`'s plugin, since `bevy::math` does not contain a plugin. - `bevy_render` - `struct Color` - `struct PipelineSpecialization` - `struct ShaderSpecialization` - `enum PrimitiveTopology` - `enum IndexFormat` Not Addressed: - I am not searching for components in Bevy that are _not_ reflected. So if there are components that are not reflected that should be reflected, that will need to be figured out in another PR. - I only added `#[reflect(...)]` or `#[reflect_value(...)]` entries for the set of four traits { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } _if they were derived via `#[derive(...)]`_. I did not look for manual trait implementations of the same set of four, nor did I consider any traits outside the four. Are those other possibilities something that needs to be looked into?
2021-03-09 23:39:41 +00:00
impl GetTypeRegistration for Cow<'static, str> {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Cow<'static, str>>();
registration.insert::<ReflectDeserialize>(FromType::<Cow<'static, str>>::from_type());
bevy_reflect: `ReflectFromPtr` to create `&dyn Reflect` from a `*const ()` (#4475) # Objective https://github.com/bevyengine/bevy/pull/4447 adds functions that can fetch resources/components as `*const ()` ptr by providing the `ComponentId`. This alone is not enough for them to be usable safely with reflection, because there is no general way to go from the raw pointer to a `&dyn Reflect` which is the pointer + a pointer to the VTable of the `Reflect` impl. By adding a `ReflectFromPtr` type that is included in the type type registration when deriving `Reflect`, safe functions can be implemented in scripting languages that don't assume a type layout and can access the component data via reflection: ```rust #[derive(Reflect)] struct StringResource { value: String } ``` ```lua local res_id = world:resource_id_by_name("example::StringResource") local res = world:resource(res_id) print(res.value) ``` ## Solution 1. add a `ReflectFromPtr` type with a `FromType<T: Reflect>` implementation and the following methods: - ` pub unsafe fn as_reflect_ptr<'a>(&self, val: Ptr<'a>) -> &'a dyn Reflect` - ` pub unsafe fn as_reflect_ptr_mut<'a>(&self, val: PtrMut<'a>) -> &'a mud dyn Reflect` Safety requirements of the methods are that you need to check that the `ReflectFromPtr` was constructed for the correct type. 2. add that type to the `TypeRegistration` in the `GetTypeRegistration` impl generated by `#[derive(Reflect)]`. This is different to other reflected traits because it doesn't need `#[reflect(ReflectReflectFromPtr)]` which IMO should be there by default. Co-authored-by: Jakob Hellermann <hellermann@sipgate.de> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-19 23:00:34 +00:00
registration.insert::<ReflectFromPtr>(FromType::<Cow<'static, str>>::from_type());
registration.insert::<ReflectSerialize>(FromType::<Cow<'static, str>>::from_type());
Reflection cleanup (#1536) This is an effort to provide the correct `#[reflect_value(...)]` attributes where they are needed. Supersedes #1533 and resolves #1528. --- I am working under the following assumptions (thanks to @bjorn3 and @Davier for advice here): - Any `enum` that derives `Reflect` and one or more of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } needs a `#[reflect_value(...)]` attribute containing the same subset of { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } that is present on the derive. - Same as above for `struct` and `#[reflect(...)]`, respectively. - If a `struct` is used as a component, it should also have `#[reflect(Component)]` - All reflected types should be registered in their plugins I treated the following as components (added `#[reflect(Component)]` if necessary): - `bevy_render` - `struct RenderLayers` - `bevy_transform` - `struct GlobalTransform` - `struct Parent` - `struct Transform` - `bevy_ui` - `struct Style` Not treated as components: - `bevy_math` - `struct Size<T>` - `struct Rect<T>` - Note: The updates for `Size<T>` and `Rect<T>` in `bevy::math::geometry` required using @Davier's suggestion to add `+ PartialEq` to the trait bound. I then registered the specific types used over in `bevy_ui` such as `Size<Val>`, etc. in `bevy_ui`'s plugin, since `bevy::math` does not contain a plugin. - `bevy_render` - `struct Color` - `struct PipelineSpecialization` - `struct ShaderSpecialization` - `enum PrimitiveTopology` - `enum IndexFormat` Not Addressed: - I am not searching for components in Bevy that are _not_ reflected. So if there are components that are not reflected that should be reflected, that will need to be figured out in another PR. - I only added `#[reflect(...)]` or `#[reflect_value(...)]` entries for the set of four traits { `Serialize`, `Deserialize`, `PartialEq`, `Hash` } _if they were derived via `#[derive(...)]`_. I did not look for manual trait implementations of the same set of four, nor did I consider any traits outside the four. Are those other possibilities something that needs to be looked into?
2021-03-09 23:39:41 +00:00
registration
}
}
impl FromReflect for Cow<'static, str> {
fn from_reflect(reflect: &dyn crate::Reflect) -> Option<Self> {
Some(
reflect
.as_any()
.downcast_ref::<Cow<'static, str>>()?
.clone(),
)
}
}
impl Reflect for &'static Path {
fn type_name(&self) -> &str {
std::any::type_name::<Self>()
}
fn get_type_info(&self) -> &'static TypeInfo {
<Self as Typed>::type_info()
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
self
}
fn as_reflect(&self) -> &dyn Reflect {
self
}
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}
fn apply(&mut self, value: &dyn Reflect) {
let value = value.as_any();
if let Some(&value) = value.downcast_ref::<Self>() {
*self = value;
} else {
panic!("Value is not a {}.", std::any::type_name::<Self>());
}
}
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
*self = value.take()?;
Ok(())
}
fn reflect_ref(&self) -> ReflectRef {
ReflectRef::Value(self)
}
fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::Value(self)
}
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
ReflectOwned::Value(self)
}
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(*self)
}
fn reflect_hash(&self) -> Option<u64> {
let mut hasher = reflect_hasher();
Hash::hash(&std::any::Any::type_id(self), &mut hasher);
Hash::hash(self, &mut hasher);
Some(hasher.finish())
}
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
let value = value.as_any();
if let Some(value) = value.downcast_ref::<Self>() {
Some(std::cmp::PartialEq::eq(self, value))
} else {
Some(false)
}
}
}
impl Typed for &'static Path {
fn type_info() -> &'static TypeInfo {
static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
CELL.get_or_set(|| TypeInfo::Value(ValueInfo::new::<Self>()))
}
}
impl GetTypeRegistration for &'static Path {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Self>();
registration.insert::<ReflectFromPtr>(FromType::<Self>::from_type());
registration
}
}
impl FromReflect for &'static Path {
fn from_reflect(reflect: &dyn crate::Reflect) -> Option<Self> {
reflect.as_any().downcast_ref::<Self>().copied()
}
}
#[cfg(test)]
mod tests {
use crate as bevy_reflect;
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
use crate::{
Enum, FromReflect, Reflect, ReflectSerialize, TypeInfo, TypeRegistry, Typed, VariantInfo,
VariantType,
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
};
use bevy_utils::HashMap;
use bevy_utils::{Duration, Instant};
use std::f32::consts::{PI, TAU};
use std::path::Path;
#[test]
fn can_serialize_duration() {
let mut type_registry = TypeRegistry::default();
type_registry.register::<Duration>();
let reflect_serialize = type_registry
.get_type_data::<ReflectSerialize>(std::any::TypeId::of::<Duration>())
.unwrap();
let _serializable = reflect_serialize.get_serializable(&Duration::ZERO);
}
#[test]
fn should_partial_eq_char() {
let a: &dyn Reflect = &'x';
let b: &dyn Reflect = &'x';
let c: &dyn Reflect = &'o';
assert!(a.reflect_partial_eq(b).unwrap_or_default());
assert!(!a.reflect_partial_eq(c).unwrap_or_default());
}
#[test]
fn should_partial_eq_i32() {
let a: &dyn Reflect = &123_i32;
let b: &dyn Reflect = &123_i32;
let c: &dyn Reflect = &321_i32;
assert!(a.reflect_partial_eq(b).unwrap_or_default());
assert!(!a.reflect_partial_eq(c).unwrap_or_default());
}
#[test]
fn should_partial_eq_f32() {
let a: &dyn Reflect = &PI;
let b: &dyn Reflect = &PI;
let c: &dyn Reflect = &TAU;
assert!(a.reflect_partial_eq(b).unwrap_or_default());
assert!(!a.reflect_partial_eq(c).unwrap_or_default());
}
#[test]
fn should_partial_eq_string() {
let a: &dyn Reflect = &String::from("Hello");
let b: &dyn Reflect = &String::from("Hello");
let c: &dyn Reflect = &String::from("World");
assert!(a.reflect_partial_eq(b).unwrap_or_default());
assert!(!a.reflect_partial_eq(c).unwrap_or_default());
}
#[test]
fn should_partial_eq_vec() {
let a: &dyn Reflect = &vec![1, 2, 3];
let b: &dyn Reflect = &vec![1, 2, 3];
let c: &dyn Reflect = &vec![3, 2, 1];
assert!(a.reflect_partial_eq(b).unwrap_or_default());
assert!(!a.reflect_partial_eq(c).unwrap_or_default());
}
#[test]
fn should_partial_eq_hash_map() {
let mut a = HashMap::new();
a.insert(0usize, 1.23_f64);
let b = a.clone();
let mut c = HashMap::new();
c.insert(0usize, 3.21_f64);
let a: &dyn Reflect = &a;
let b: &dyn Reflect = &b;
let c: &dyn Reflect = &c;
assert!(a.reflect_partial_eq(b).unwrap_or_default());
assert!(!a.reflect_partial_eq(c).unwrap_or_default());
}
#[test]
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
fn should_partial_eq_option() {
let a: &dyn Reflect = &Some(123);
let b: &dyn Reflect = &Some(123);
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
assert_eq!(Some(true), a.reflect_partial_eq(b));
}
#[test]
fn option_should_impl_enum() {
let mut value = Some(123usize);
assert!(value
.reflect_partial_eq(&Some(123usize))
.unwrap_or_default());
assert!(!value
.reflect_partial_eq(&Some(321usize))
.unwrap_or_default());
assert_eq!("Some", value.variant_name());
assert_eq!("core::option::Option<usize>::Some", value.variant_path());
if value.is_variant(VariantType::Tuple) {
if let Some(field) = value
.field_at_mut(0)
.and_then(|field| field.downcast_mut::<usize>())
{
*field = 321;
}
} else {
panic!("expected `VariantType::Tuple`");
}
assert_eq!(Some(321), value);
}
#[test]
fn option_should_from_reflect() {
#[derive(Reflect, FromReflect, PartialEq, Debug)]
struct Foo(usize);
let expected = Some(Foo(123));
let output = <Option<Foo> as FromReflect>::from_reflect(&expected).unwrap();
assert_eq!(expected, output);
}
#[test]
fn option_should_apply() {
#[derive(Reflect, FromReflect, PartialEq, Debug)]
struct Foo(usize);
// === None on None === //
let patch = None::<Foo>;
let mut value = None;
Reflect::apply(&mut value, &patch);
assert_eq!(patch, value, "None apply onto None");
// === Some on None === //
let patch = Some(Foo(123));
let mut value = None;
Reflect::apply(&mut value, &patch);
assert_eq!(patch, value, "Some apply onto None");
// === None on Some === //
let patch = None::<Foo>;
let mut value = Some(Foo(321));
Reflect::apply(&mut value, &patch);
assert_eq!(patch, value, "None apply onto Some");
// === Some on Some === //
let patch = Some(Foo(123));
let mut value = Some(Foo(321));
Reflect::apply(&mut value, &patch);
assert_eq!(patch, value, "Some apply onto Some");
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
#[test]
fn option_should_impl_typed() {
type MyOption = Option<i32>;
let info = MyOption::type_info();
if let TypeInfo::Enum(info) = info {
assert_eq!(
"None",
info.variant_at(0).unwrap().name(),
"Expected `None` to be variant at index `0`"
);
assert_eq!(
"Some",
info.variant_at(1).unwrap().name(),
"Expected `Some` to be variant at index `1`"
);
assert_eq!("Some", info.variant("Some").unwrap().name());
if let VariantInfo::Tuple(variant) = info.variant("Some").unwrap() {
assert!(
variant.field_at(0).unwrap().is::<i32>(),
"Expected `Some` variant to contain `i32`"
);
assert!(
variant.field_at(1).is_none(),
"Expected `Some` variant to only contain 1 field"
);
} else {
panic!("Expected `VariantInfo::Tuple`");
}
} else {
panic!("Expected `TypeInfo::Enum`");
}
}
#[test]
fn nonzero_usize_impl_reflect_from_reflect() {
let a: &dyn Reflect = &std::num::NonZeroUsize::new(42).unwrap();
let b: &dyn Reflect = &std::num::NonZeroUsize::new(42).unwrap();
assert!(a.reflect_partial_eq(b).unwrap_or_default());
let forty_two: std::num::NonZeroUsize = crate::FromReflect::from_reflect(a).unwrap();
assert_eq!(forty_two, std::num::NonZeroUsize::new(42).unwrap());
}
#[test]
fn instant_should_from_reflect() {
let expected = Instant::now();
let output = <Instant as FromReflect>::from_reflect(&expected).unwrap();
assert_eq!(expected, output);
}
#[test]
fn path_should_from_reflect() {
let path = Path::new("hello_world.rs");
let output = <&'static Path as FromReflect>::from_reflect(&path).unwrap();
assert_eq!(path, output);
}
}