2022-10-31 16:35:22 +00:00
|
|
|
use crate::std_traits::ReflectDefault;
|
2023-05-08 19:19:19 +00:00
|
|
|
use crate::{self as bevy_reflect, ReflectFromPtr, ReflectFromReflect, 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
|
|
|
};
|
|
|
|
|
2023-02-17 15:37:35 +00:00
|
|
|
use crate::utility::{reflect_hasher, GenericTypeInfoCell, NonGenericTypeInfoCell};
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value};
|
2023-02-27 21:37:36 +00:00
|
|
|
use bevy_utils::HashSet;
|
Derived `Copy` trait for `bevy_input` events, `Serialize`/`Deserialize` for events in `bevy_input` and `bevy_windows`, `PartialEq` for events in both, and `Eq` where possible in both. (#6023)
# Objective
Add traits to events in `bevy_input` and `bevy_windows`: `Copy`, `Serialize`/`Deserialize`, `PartialEq`, and `Eq`, as requested in https://github.com/bevyengine/bevy/issues/6022, https://github.com/bevyengine/bevy/issues/6023, https://github.com/bevyengine/bevy/issues/6024.
## Solution
Added the traits to events in `bevy_input` and `bevy_windows`. Added dependency of `serde` in `Cargo.toml` of `bevy_input`.
## Migration Guide
If one has been `.clone()`'ing `bevy_input` events, Clippy will now complain about that. Just remove `.clone()` to solve.
## Other Notes
Some events in `bevy_input` had `f32` fields, so `Eq` trait was not derived for them.
Some events in `bevy_windows` had `String` fields, so `Copy` trait was not derived for them.
Co-authored-by: targrub <62773321+targrub@users.noreply.github.com>
2022-09-20 18:24:00 +00:00
|
|
|
use bevy_utils::{Duration, Instant};
|
2023-05-08 19:19:19 +00:00
|
|
|
use std::fmt;
|
2021-02-01 00:35:23 +00:00
|
|
|
use std::{
|
|
|
|
any::Any,
|
|
|
|
borrow::Cow,
|
2022-12-11 18:22:08 +00:00
|
|
|
collections::VecDeque,
|
2022-11-27 17:28:06 +00:00
|
|
|
ffi::OsString,
|
2023-02-27 21:37:36 +00:00
|
|
|
hash::{BuildHasher, Hash, Hasher},
|
2022-08-04 22:09:49 +00:00
|
|
|
num::{
|
|
|
|
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
|
|
|
|
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
|
|
|
|
},
|
2022-08-30 20:51:21 +00:00
|
|
|
ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive},
|
2022-11-27 17:28:06 +00:00
|
|
|
path::{Path, PathBuf},
|
2021-02-01 00:35:23 +00:00
|
|
|
};
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2022-10-31 16:35:22 +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>());
|
2022-08-30 21:39:48 +00:00
|
|
|
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>());
|
2022-08-30 20:51:21 +00:00
|
|
|
impl_reflect_value!(RangeFull());
|
2022-10-31 16:35:22 +00:00
|
|
|
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));
|
2022-08-04 22:09:49 +00:00
|
|
|
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
|
|
|
|
2022-11-27 17:28:06 +00:00
|
|
|
// `Serialize` and `Deserialize` only for platforms supported by serde:
|
2022-10-08 17:02:21 +00:00
|
|
|
// 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));
|
2022-11-27 17:28:06 +00:00
|
|
|
#[cfg(not(any(unix, windows)))]
|
|
|
|
impl_reflect_value!(OsString(Debug, Hash, PartialEq));
|
2022-10-08 17:02:21 +00:00
|
|
|
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
impl_from_reflect_value!(bool);
|
2022-05-17 23:45:09 +00:00
|
|
|
impl_from_reflect_value!(char);
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
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);
|
2022-11-27 17:28:06 +00:00
|
|
|
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>);
|
2022-08-30 20:51:21 +00:00
|
|
|
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);
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
impl_from_reflect_value!(Duration);
|
2022-08-17 00:21:15 +00:00
|
|
|
impl_from_reflect_value!(Instant);
|
2022-08-04 22:09:49 +00:00
|
|
|
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);
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
|
2022-12-11 18:22:08 +00:00
|
|
|
macro_rules! impl_reflect_for_veclike {
|
2023-01-09 19:47:07 +00:00
|
|
|
($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 {
|
2022-12-11 18:22:08 +00:00
|
|
|
#[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)
|
|
|
|
}
|
|
|
|
|
2023-01-09 19:47:07 +00:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2022-12-11 18:22:08 +00:00
|
|
|
fn push(&mut self, value: Box<dyn Reflect>) {
|
2023-01-11 16:25:37 +00:00
|
|
|
let value = T::take_from_reflect(value).unwrap_or_else(|value| {
|
|
|
|
panic!(
|
|
|
|
"Attempted to push invalid value of type {}.",
|
|
|
|
value.type_name()
|
|
|
|
)
|
2022-12-11 18:22:08 +00:00
|
|
|
});
|
|
|
|
$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()
|
|
|
|
}
|
2022-12-11 18:22:08 +00:00
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2022-12-11 18:22:08 +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
|
|
|
|
bevy_reflect: Better proxies (#6971)
# Objective
> This PR is based on discussion from #6601
The Dynamic types (e.g. `DynamicStruct`, `DynamicList`, etc.) act as
both:
1. Dynamic containers which may hold any arbitrary data
2. Proxy types which may represent any other type
Currently, the only way we can represent the proxy-ness of a Dynamic is
by giving it a name.
```rust
// This is just a dynamic container
let mut data = DynamicStruct::default();
// This is a "proxy"
data.set_name(std::any::type_name::<Foo>());
```
This type name is the only way we check that the given Dynamic is a
proxy of some other type. When we need to "assert the type" of a `dyn
Reflect`, we call `Reflect::type_name` on it. However, because we're
only using a string to denote the type, we run into a few gotchas and
limitations.
For example, hashing a Dynamic proxy may work differently than the type
it proxies:
```rust
#[derive(Reflect, Hash)]
#[reflect(Hash)]
struct Foo(i32);
let concrete = Foo(123);
let dynamic = concrete.clone_dynamic();
let concrete_hash = concrete.reflect_hash();
let dynamic_hash = dynamic.reflect_hash();
// The hashes are not equal because `concrete` uses its own `Hash` impl
// while `dynamic` uses a reflection-based hashing algorithm
assert_ne!(concrete_hash, dynamic_hash);
```
Because the Dynamic proxy only knows about the name of the type, it's
unaware of any other information about it. This means it also differs on
`Reflect::reflect_partial_eq`, and may include ignored or skipped fields
in places the concrete type wouldn't.
## Solution
Rather than having Dynamics pass along just the type name of proxied
types, we can instead have them pass around the `TypeInfo`.
Now all Dynamic types contain an `Option<&'static TypeInfo>` rather than
a `String`:
```diff
pub struct DynamicTupleStruct {
- type_name: String,
+ represented_type: Option<&'static TypeInfo>,
fields: Vec<Box<dyn Reflect>>,
}
```
By changing `Reflect::get_type_info` to
`Reflect::represented_type_info`, hopefully we make this behavior a
little clearer. And to account for `None` values on these dynamic types,
`Reflect::represented_type_info` now returns `Option<&'static
TypeInfo>`.
```rust
let mut data = DynamicTupleStruct::default();
// Not proxying any specific type
assert!(dyn_tuple_struct.represented_type_info().is_none());
let type_info = <Foo as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
// Alternatively:
// let dyn_tuple_struct = foo.clone_dynamic();
// Now we're proxying `Foo`
assert!(dyn_tuple_struct.represented_type_info().is_some());
```
This means that we can have full access to all the static type
information for the proxied type. Future work would include
transitioning more static type information (trait impls, attributes,
etc.) over to the `TypeInfo` so it can actually be utilized by Dynamic
proxies.
### Alternatives & Rationale
> **Note**
> These alternatives were written when this PR was first made using a
`Proxy` trait. This trait has since been removed.
<details>
<summary>View</summary>
#### Alternative: The `Proxy<T>` Approach
I had considered adding something like a `Proxy<T>` type where `T` would
be the Dynamic and would contain the proxied type information.
This was nice in that it allows us to explicitly determine whether
something is a proxy or not at a type level. `Proxy<DynamicStruct>`
proxies a struct. Makes sense.
The reason I didn't go with this approach is because (1) tuples, (2)
complexity, and (3) `PartialReflect`.
The `DynamicTuple` struct allows us to represent tuples at runtime. It
also allows us to do something you normally can't with tuples: add new
fields. Because of this, adding a field immediately invalidates the
proxy (e.g. our info for `(i32, i32)` doesn't apply to `(i32, i32,
NewField)`). By going with this PR's approach, we can just remove the
type info on `DynamicTuple` when that happens. However, with the
`Proxy<T>` approach, it becomes difficult to represent this behavior—
we'd have to completely control how we access data for `T` for each `T`.
Secondly, it introduces some added complexities (aside from the manual
impls for each `T`). Does `Proxy<T>` impl `Reflect`? Likely yes, if we
want to represent it as `dyn Reflect`. What `TypeInfo` do we give it?
How would we forward reflection methods to the inner type (remember, we
don't have specialization)? How do we separate this from Dynamic types?
And finally, how do all this in a way that's both logical and intuitive
for users?
Lastly, introducing a `Proxy` trait rather than a `Proxy<T>` struct is
actually more inline with the [Unique Reflect
RFC](https://github.com/bevyengine/rfcs/pull/56). In a way, the `Proxy`
trait is really one part of the `PartialReflect` trait introduced in
that RFC (it's technically not in that RFC but it fits well with it),
where the `PartialReflect` serves as a way for proxies to work _like_
concrete types without having full access to everything a concrete
`Reflect` type can do. This would help bridge the gap between the
current state of the crate and the implementation of that RFC.
All that said, this is still a viable solution. If the community
believes this is the better path forward, then we can do that instead.
These were just my reasons for not initially going with it in this PR.
#### Alternative: The Type Registry Approach
The `Proxy` trait is great and all, but how does it solve the original
problem? Well, it doesn't— yet!
The goal would be to start moving information from the derive macro and
its attributes to the generated `TypeInfo` since these are known
statically and shouldn't change. For example, adding `ignored: bool` to
`[Un]NamedField` or a list of impls.
However, there is another way of storing this information. This is, of
course, one of the uses of the `TypeRegistry`. If we're worried about
Dynamic proxies not aligning with their concrete counterparts, we could
move more type information to the registry and require its usage.
For example, we could replace `Reflect::reflect_hash(&self)` with
`Reflect::reflect_hash(&self, registry: &TypeRegistry)`.
That's not the _worst_ thing in the world, but it is an ergonomics loss.
Additionally, other attributes may have their own requirements, further
restricting what's possible without the registry. The `Reflect::apply`
method will require the registry as well now. Why? Well because the
`map_apply` function used for the `Reflect::apply` impls on `Map` types
depends on `Map::insert_boxed`, which (at least for `DynamicMap`)
requires `Reflect::reflect_hash`. The same would apply when adding
support for reflection-based diffing, which will require
`Reflect::reflect_partial_eq`.
Again, this is a totally viable alternative. I just chose not to go with
it for the reasons above. If we want to go with it, then we can close
this PR and we can pursue this alternative instead.
#### Downsides
Just to highlight a quick potential downside (likely needs more
investigation): retrieving the `TypeInfo` requires acquiring a lock on
the `GenericTypeInfoCell` used by the `Typed` impls for generic types
(non-generic types use a `OnceBox which should be faster). I am not sure
how much of a performance hit that is and will need to run some
benchmarks to compare against.
</details>
### Open Questions
1. Should we use `Cow<'static, TypeInfo>` instead? I think that might be
easier for modding? Perhaps, in that case, we need to update
`Typed::type_info` and friends as well?
2. Are the alternatives better than the approach this PR takes? Are
there other alternatives?
---
## Changelog
### Changed
- `Reflect::get_type_info` has been renamed to
`Reflect::represented_type_info`
- This method now returns `Option<&'static TypeInfo>` rather than just
`&'static TypeInfo`
### Added
- Added `Reflect::is_dynamic` method to indicate when a type is dynamic
- Added a `set_represented_type` method on all dynamic types
### Removed
- Removed `TypeInfo::Dynamic` (use `Reflect::is_dynamic` instead)
- Removed `Typed` impls for all dynamic types
## Migration Guide
- The Dynamic types no longer take a string type name. Instead, they
require a static reference to `TypeInfo`:
```rust
#[derive(Reflect)]
struct MyTupleStruct(f32, f32);
let mut dyn_tuple_struct = DynamicTupleStruct::default();
dyn_tuple_struct.insert(1.23_f32);
dyn_tuple_struct.insert(3.21_f32);
// BEFORE:
let type_name = std::any::type_name::<MyTupleStruct>();
dyn_tuple_struct.set_name(type_name);
// AFTER:
let type_info = <MyTupleStruct as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
```
- `Reflect::get_type_info` has been renamed to
`Reflect::represented_type_info` and now also returns an
`Option<&'static TypeInfo>` (instead of just `&'static TypeInfo`):
```rust
// BEFORE:
let info: &'static TypeInfo = value.get_type_info();
// AFTER:
let info: &'static TypeInfo = value.represented_type_info().unwrap();
```
- `TypeInfo::Dynamic` and `DynamicInfo` has been removed. Use
`Reflect::is_dynamic` instead:
```rust
// BEFORE:
if matches!(value.get_type_info(), TypeInfo::Dynamic) {
// ...
}
// AFTER:
if value.is_dynamic() {
// ...
}
```
---------
Co-authored-by: radiish <cb.setho@gmail.com>
2023-04-26 12:17:46 +00:00
|
|
|
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
|
|
|
Some(<Self as Typed>::type_info())
|
2022-12-11 18:22:08 +00:00
|
|
|
}
|
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
|
|
|
|
2022-12-11 18:22:08 +00:00
|
|
|
fn into_any(self: Box<Self>) -> Box<dyn Any> {
|
|
|
|
self
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2022-12-11 18:22:08 +00:00
|
|
|
fn as_any(&self) -> &dyn Any {
|
|
|
|
self
|
|
|
|
}
|
Make `Reflect` safe to implement (#5010)
# Objective
Currently, `Reflect` is unsafe to implement because of a contract in which `any` and `any_mut` must return `self`, or `downcast` will cause UB. This PR makes `Reflect` safe, makes `downcast` not use unsafe, and eliminates this contract.
## Solution
This PR adds a method to `Reflect`, `any`. It also renames the old `any` to `as_any`.
`any` now takes a `Box<Self>` and returns a `Box<dyn Any>`.
---
## Changelog
### Added:
- `any()` method
- `represents()` method
### Changed:
- `Reflect` is now a safe trait
- `downcast()` is now safe
- The old `any` is now called `as_any`, and `any_mut` is now `as_mut_any`
## Migration Guide
- Reflect derives should not have to change anything
- Manual reflect impls will need to remove the `unsafe` keyword, add `any()` implementations, and rename the old `any` and `any_mut` to `as_any` and `as_mut_any`.
- Calls to `any`/`any_mut` must be changed to `as_any`/`as_mut_any`
## Points of discussion:
- Should renaming `any` be avoided and instead name the new method `any_box`?
- ~~Could there be a performance regression from avoiding the unsafe? I doubt it, but this change does seem to introduce redundant checks.~~
- ~~Could/should `is` and `type_id()` be implemented differently? For example, moving `is` onto `Reflect` as an `fn(&self, TypeId) -> bool`~~
Co-authored-by: PROMETHIA-27 <42193387+PROMETHIA-27@users.noreply.github.com>
2022-06-27 16:52:25 +00:00
|
|
|
|
2022-12-11 18:22:08 +00:00
|
|
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
|
|
|
self
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2022-12-11 18:22:08 +00:00
|
|
|
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
|
|
|
|
self
|
|
|
|
}
|
2022-11-07 02:11:16 +00:00
|
|
|
|
2022-12-11 18:22:08 +00:00
|
|
|
fn as_reflect(&self) -> &dyn Reflect {
|
|
|
|
self
|
|
|
|
}
|
2022-04-25 13:54:48 +00:00
|
|
|
|
2022-12-11 18:22:08 +00:00
|
|
|
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
|
|
|
|
self
|
|
|
|
}
|
2022-04-25 13:54:48 +00:00
|
|
|
|
2022-12-11 18:22:08 +00:00
|
|
|
fn apply(&mut self, value: &dyn Reflect) {
|
|
|
|
crate::list_apply(self, value);
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2022-12-11 18:22:08 +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
|
|
|
|
2022-12-11 18:22:08 +00:00
|
|
|
fn reflect_ref(&self) -> ReflectRef {
|
|
|
|
ReflectRef::List(self)
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2022-12-11 18:22:08 +00:00
|
|
|
fn reflect_mut(&mut self) -> ReflectMut {
|
|
|
|
ReflectMut::List(self)
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2022-12-11 18:22:08 +00:00
|
|
|
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
|
|
|
|
ReflectOwned::List(self)
|
|
|
|
}
|
2022-11-06 16:58:38 +00:00
|
|
|
|
2022-12-11 18:22:08 +00:00
|
|
|
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())
|
2022-12-11 18:22:08 +00:00
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2022-12-11 18:22:08 +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)
|
2022-12-11 18:22:08 +00:00
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2022-12-11 18:22:08 +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
|
|
|
|
2022-12-11 18:22:08 +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
|
|
|
|
2022-12-11 18:22:08 +00:00
|
|
|
impl<T: FromReflect> GetTypeRegistration for $ty {
|
|
|
|
fn get_type_registration() -> TypeRegistration {
|
2023-02-06 12:58:21 +00:00
|
|
|
let mut registration = TypeRegistration::of::<$ty>();
|
|
|
|
registration.insert::<ReflectFromPtr>(FromType::<$ty>::from_type());
|
2022-12-11 18:22:08 +00:00
|
|
|
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
|
|
|
|
2022-12-11 18:22:08 +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
|
|
|
|
}
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
}
|
|
|
|
}
|
2022-12-11 18:22:08 +00:00
|
|
|
};
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
}
|
|
|
|
|
2023-01-09 19:47:07 +00:00
|
|
|
impl_reflect_for_veclike!(Vec<T>, Vec::insert, Vec::remove, Vec::push, Vec::pop, [T]);
|
2022-12-11 18:22:08 +00:00
|
|
|
impl_reflect_for_veclike!(
|
|
|
|
VecDeque<T>,
|
2023-01-09 19:47:07 +00:00
|
|
|
VecDeque::insert,
|
|
|
|
VecDeque::remove,
|
2022-12-11 18:22:08 +00:00
|
|
|
VecDeque::push_back,
|
|
|
|
VecDeque::pop_back,
|
|
|
|
VecDeque::<T>
|
|
|
|
);
|
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
macro_rules! impl_reflect_for_hashmap {
|
|
|
|
($ty:ty) => {
|
|
|
|
impl<K, V, S> Map for $ty
|
|
|
|
where
|
|
|
|
K: FromReflect + Eq + Hash,
|
|
|
|
V: FromReflect,
|
|
|
|
S: BuildHasher + Send + Sync + 'static,
|
|
|
|
{
|
|
|
|
fn get(&self, key: &dyn Reflect) -> Option<&dyn Reflect> {
|
|
|
|
key.downcast_ref::<K>()
|
|
|
|
.and_then(|key| Self::get(self, key))
|
|
|
|
.map(|value| value as &dyn Reflect)
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
fn get_mut(&mut self, key: &dyn Reflect) -> Option<&mut dyn Reflect> {
|
|
|
|
key.downcast_ref::<K>()
|
|
|
|
.and_then(move |key| Self::get_mut(self, key))
|
|
|
|
.map(|value| value as &mut dyn Reflect)
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
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))
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
fn len(&self) -> usize {
|
|
|
|
Self::len(self)
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
fn iter(&self) -> MapIter {
|
|
|
|
MapIter {
|
|
|
|
map: self,
|
|
|
|
index: 0,
|
|
|
|
}
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2023-02-27 21:37:36 +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()
|
|
|
|
}
|
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
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
fn clone_dynamic(&self) -> DynamicMap {
|
|
|
|
let mut dynamic_map = DynamicMap::default();
|
bevy_reflect: Better proxies (#6971)
# Objective
> This PR is based on discussion from #6601
The Dynamic types (e.g. `DynamicStruct`, `DynamicList`, etc.) act as
both:
1. Dynamic containers which may hold any arbitrary data
2. Proxy types which may represent any other type
Currently, the only way we can represent the proxy-ness of a Dynamic is
by giving it a name.
```rust
// This is just a dynamic container
let mut data = DynamicStruct::default();
// This is a "proxy"
data.set_name(std::any::type_name::<Foo>());
```
This type name is the only way we check that the given Dynamic is a
proxy of some other type. When we need to "assert the type" of a `dyn
Reflect`, we call `Reflect::type_name` on it. However, because we're
only using a string to denote the type, we run into a few gotchas and
limitations.
For example, hashing a Dynamic proxy may work differently than the type
it proxies:
```rust
#[derive(Reflect, Hash)]
#[reflect(Hash)]
struct Foo(i32);
let concrete = Foo(123);
let dynamic = concrete.clone_dynamic();
let concrete_hash = concrete.reflect_hash();
let dynamic_hash = dynamic.reflect_hash();
// The hashes are not equal because `concrete` uses its own `Hash` impl
// while `dynamic` uses a reflection-based hashing algorithm
assert_ne!(concrete_hash, dynamic_hash);
```
Because the Dynamic proxy only knows about the name of the type, it's
unaware of any other information about it. This means it also differs on
`Reflect::reflect_partial_eq`, and may include ignored or skipped fields
in places the concrete type wouldn't.
## Solution
Rather than having Dynamics pass along just the type name of proxied
types, we can instead have them pass around the `TypeInfo`.
Now all Dynamic types contain an `Option<&'static TypeInfo>` rather than
a `String`:
```diff
pub struct DynamicTupleStruct {
- type_name: String,
+ represented_type: Option<&'static TypeInfo>,
fields: Vec<Box<dyn Reflect>>,
}
```
By changing `Reflect::get_type_info` to
`Reflect::represented_type_info`, hopefully we make this behavior a
little clearer. And to account for `None` values on these dynamic types,
`Reflect::represented_type_info` now returns `Option<&'static
TypeInfo>`.
```rust
let mut data = DynamicTupleStruct::default();
// Not proxying any specific type
assert!(dyn_tuple_struct.represented_type_info().is_none());
let type_info = <Foo as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
// Alternatively:
// let dyn_tuple_struct = foo.clone_dynamic();
// Now we're proxying `Foo`
assert!(dyn_tuple_struct.represented_type_info().is_some());
```
This means that we can have full access to all the static type
information for the proxied type. Future work would include
transitioning more static type information (trait impls, attributes,
etc.) over to the `TypeInfo` so it can actually be utilized by Dynamic
proxies.
### Alternatives & Rationale
> **Note**
> These alternatives were written when this PR was first made using a
`Proxy` trait. This trait has since been removed.
<details>
<summary>View</summary>
#### Alternative: The `Proxy<T>` Approach
I had considered adding something like a `Proxy<T>` type where `T` would
be the Dynamic and would contain the proxied type information.
This was nice in that it allows us to explicitly determine whether
something is a proxy or not at a type level. `Proxy<DynamicStruct>`
proxies a struct. Makes sense.
The reason I didn't go with this approach is because (1) tuples, (2)
complexity, and (3) `PartialReflect`.
The `DynamicTuple` struct allows us to represent tuples at runtime. It
also allows us to do something you normally can't with tuples: add new
fields. Because of this, adding a field immediately invalidates the
proxy (e.g. our info for `(i32, i32)` doesn't apply to `(i32, i32,
NewField)`). By going with this PR's approach, we can just remove the
type info on `DynamicTuple` when that happens. However, with the
`Proxy<T>` approach, it becomes difficult to represent this behavior—
we'd have to completely control how we access data for `T` for each `T`.
Secondly, it introduces some added complexities (aside from the manual
impls for each `T`). Does `Proxy<T>` impl `Reflect`? Likely yes, if we
want to represent it as `dyn Reflect`. What `TypeInfo` do we give it?
How would we forward reflection methods to the inner type (remember, we
don't have specialization)? How do we separate this from Dynamic types?
And finally, how do all this in a way that's both logical and intuitive
for users?
Lastly, introducing a `Proxy` trait rather than a `Proxy<T>` struct is
actually more inline with the [Unique Reflect
RFC](https://github.com/bevyengine/rfcs/pull/56). In a way, the `Proxy`
trait is really one part of the `PartialReflect` trait introduced in
that RFC (it's technically not in that RFC but it fits well with it),
where the `PartialReflect` serves as a way for proxies to work _like_
concrete types without having full access to everything a concrete
`Reflect` type can do. This would help bridge the gap between the
current state of the crate and the implementation of that RFC.
All that said, this is still a viable solution. If the community
believes this is the better path forward, then we can do that instead.
These were just my reasons for not initially going with it in this PR.
#### Alternative: The Type Registry Approach
The `Proxy` trait is great and all, but how does it solve the original
problem? Well, it doesn't— yet!
The goal would be to start moving information from the derive macro and
its attributes to the generated `TypeInfo` since these are known
statically and shouldn't change. For example, adding `ignored: bool` to
`[Un]NamedField` or a list of impls.
However, there is another way of storing this information. This is, of
course, one of the uses of the `TypeRegistry`. If we're worried about
Dynamic proxies not aligning with their concrete counterparts, we could
move more type information to the registry and require its usage.
For example, we could replace `Reflect::reflect_hash(&self)` with
`Reflect::reflect_hash(&self, registry: &TypeRegistry)`.
That's not the _worst_ thing in the world, but it is an ergonomics loss.
Additionally, other attributes may have their own requirements, further
restricting what's possible without the registry. The `Reflect::apply`
method will require the registry as well now. Why? Well because the
`map_apply` function used for the `Reflect::apply` impls on `Map` types
depends on `Map::insert_boxed`, which (at least for `DynamicMap`)
requires `Reflect::reflect_hash`. The same would apply when adding
support for reflection-based diffing, which will require
`Reflect::reflect_partial_eq`.
Again, this is a totally viable alternative. I just chose not to go with
it for the reasons above. If we want to go with it, then we can close
this PR and we can pursue this alternative instead.
#### Downsides
Just to highlight a quick potential downside (likely needs more
investigation): retrieving the `TypeInfo` requires acquiring a lock on
the `GenericTypeInfoCell` used by the `Typed` impls for generic types
(non-generic types use a `OnceBox which should be faster). I am not sure
how much of a performance hit that is and will need to run some
benchmarks to compare against.
</details>
### Open Questions
1. Should we use `Cow<'static, TypeInfo>` instead? I think that might be
easier for modding? Perhaps, in that case, we need to update
`Typed::type_info` and friends as well?
2. Are the alternatives better than the approach this PR takes? Are
there other alternatives?
---
## Changelog
### Changed
- `Reflect::get_type_info` has been renamed to
`Reflect::represented_type_info`
- This method now returns `Option<&'static TypeInfo>` rather than just
`&'static TypeInfo`
### Added
- Added `Reflect::is_dynamic` method to indicate when a type is dynamic
- Added a `set_represented_type` method on all dynamic types
### Removed
- Removed `TypeInfo::Dynamic` (use `Reflect::is_dynamic` instead)
- Removed `Typed` impls for all dynamic types
## Migration Guide
- The Dynamic types no longer take a string type name. Instead, they
require a static reference to `TypeInfo`:
```rust
#[derive(Reflect)]
struct MyTupleStruct(f32, f32);
let mut dyn_tuple_struct = DynamicTupleStruct::default();
dyn_tuple_struct.insert(1.23_f32);
dyn_tuple_struct.insert(3.21_f32);
// BEFORE:
let type_name = std::any::type_name::<MyTupleStruct>();
dyn_tuple_struct.set_name(type_name);
// AFTER:
let type_info = <MyTupleStruct as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
```
- `Reflect::get_type_info` has been renamed to
`Reflect::represented_type_info` and now also returns an
`Option<&'static TypeInfo>` (instead of just `&'static TypeInfo`):
```rust
// BEFORE:
let info: &'static TypeInfo = value.get_type_info();
// AFTER:
let info: &'static TypeInfo = value.represented_type_info().unwrap();
```
- `TypeInfo::Dynamic` and `DynamicInfo` has been removed. Use
`Reflect::is_dynamic` instead:
```rust
// BEFORE:
if matches!(value.get_type_info(), TypeInfo::Dynamic) {
// ...
}
// AFTER:
if value.is_dynamic() {
// ...
}
```
---------
Co-authored-by: radiish <cb.setho@gmail.com>
2023-04-26 12:17:46 +00:00
|
|
|
dynamic_map.set_represented_type(self.get_represented_type_info());
|
2023-02-27 21:37:36 +00:00
|
|
|
for (k, v) in self {
|
2023-04-22 02:55:53 +00:00
|
|
|
let key = K::from_reflect(k).unwrap_or_else(|| {
|
|
|
|
panic!("Attempted to clone invalid key of type {}.", k.type_name())
|
|
|
|
});
|
|
|
|
dynamic_map.insert_boxed(Box::new(key), v.clone_value());
|
2023-02-27 21:37:36 +00:00
|
|
|
}
|
|
|
|
dynamic_map
|
|
|
|
}
|
2022-07-04 13:04:19 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
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>)
|
|
|
|
}
|
2022-11-14 21:03:39 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
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
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
impl<K, V, S> Reflect for $ty
|
|
|
|
where
|
|
|
|
K: FromReflect + Eq + Hash,
|
|
|
|
V: FromReflect,
|
|
|
|
S: BuildHasher + Send + Sync + 'static,
|
|
|
|
{
|
|
|
|
fn type_name(&self) -> &str {
|
|
|
|
std::any::type_name::<Self>()
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
bevy_reflect: Better proxies (#6971)
# Objective
> This PR is based on discussion from #6601
The Dynamic types (e.g. `DynamicStruct`, `DynamicList`, etc.) act as
both:
1. Dynamic containers which may hold any arbitrary data
2. Proxy types which may represent any other type
Currently, the only way we can represent the proxy-ness of a Dynamic is
by giving it a name.
```rust
// This is just a dynamic container
let mut data = DynamicStruct::default();
// This is a "proxy"
data.set_name(std::any::type_name::<Foo>());
```
This type name is the only way we check that the given Dynamic is a
proxy of some other type. When we need to "assert the type" of a `dyn
Reflect`, we call `Reflect::type_name` on it. However, because we're
only using a string to denote the type, we run into a few gotchas and
limitations.
For example, hashing a Dynamic proxy may work differently than the type
it proxies:
```rust
#[derive(Reflect, Hash)]
#[reflect(Hash)]
struct Foo(i32);
let concrete = Foo(123);
let dynamic = concrete.clone_dynamic();
let concrete_hash = concrete.reflect_hash();
let dynamic_hash = dynamic.reflect_hash();
// The hashes are not equal because `concrete` uses its own `Hash` impl
// while `dynamic` uses a reflection-based hashing algorithm
assert_ne!(concrete_hash, dynamic_hash);
```
Because the Dynamic proxy only knows about the name of the type, it's
unaware of any other information about it. This means it also differs on
`Reflect::reflect_partial_eq`, and may include ignored or skipped fields
in places the concrete type wouldn't.
## Solution
Rather than having Dynamics pass along just the type name of proxied
types, we can instead have them pass around the `TypeInfo`.
Now all Dynamic types contain an `Option<&'static TypeInfo>` rather than
a `String`:
```diff
pub struct DynamicTupleStruct {
- type_name: String,
+ represented_type: Option<&'static TypeInfo>,
fields: Vec<Box<dyn Reflect>>,
}
```
By changing `Reflect::get_type_info` to
`Reflect::represented_type_info`, hopefully we make this behavior a
little clearer. And to account for `None` values on these dynamic types,
`Reflect::represented_type_info` now returns `Option<&'static
TypeInfo>`.
```rust
let mut data = DynamicTupleStruct::default();
// Not proxying any specific type
assert!(dyn_tuple_struct.represented_type_info().is_none());
let type_info = <Foo as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
// Alternatively:
// let dyn_tuple_struct = foo.clone_dynamic();
// Now we're proxying `Foo`
assert!(dyn_tuple_struct.represented_type_info().is_some());
```
This means that we can have full access to all the static type
information for the proxied type. Future work would include
transitioning more static type information (trait impls, attributes,
etc.) over to the `TypeInfo` so it can actually be utilized by Dynamic
proxies.
### Alternatives & Rationale
> **Note**
> These alternatives were written when this PR was first made using a
`Proxy` trait. This trait has since been removed.
<details>
<summary>View</summary>
#### Alternative: The `Proxy<T>` Approach
I had considered adding something like a `Proxy<T>` type where `T` would
be the Dynamic and would contain the proxied type information.
This was nice in that it allows us to explicitly determine whether
something is a proxy or not at a type level. `Proxy<DynamicStruct>`
proxies a struct. Makes sense.
The reason I didn't go with this approach is because (1) tuples, (2)
complexity, and (3) `PartialReflect`.
The `DynamicTuple` struct allows us to represent tuples at runtime. It
also allows us to do something you normally can't with tuples: add new
fields. Because of this, adding a field immediately invalidates the
proxy (e.g. our info for `(i32, i32)` doesn't apply to `(i32, i32,
NewField)`). By going with this PR's approach, we can just remove the
type info on `DynamicTuple` when that happens. However, with the
`Proxy<T>` approach, it becomes difficult to represent this behavior—
we'd have to completely control how we access data for `T` for each `T`.
Secondly, it introduces some added complexities (aside from the manual
impls for each `T`). Does `Proxy<T>` impl `Reflect`? Likely yes, if we
want to represent it as `dyn Reflect`. What `TypeInfo` do we give it?
How would we forward reflection methods to the inner type (remember, we
don't have specialization)? How do we separate this from Dynamic types?
And finally, how do all this in a way that's both logical and intuitive
for users?
Lastly, introducing a `Proxy` trait rather than a `Proxy<T>` struct is
actually more inline with the [Unique Reflect
RFC](https://github.com/bevyengine/rfcs/pull/56). In a way, the `Proxy`
trait is really one part of the `PartialReflect` trait introduced in
that RFC (it's technically not in that RFC but it fits well with it),
where the `PartialReflect` serves as a way for proxies to work _like_
concrete types without having full access to everything a concrete
`Reflect` type can do. This would help bridge the gap between the
current state of the crate and the implementation of that RFC.
All that said, this is still a viable solution. If the community
believes this is the better path forward, then we can do that instead.
These were just my reasons for not initially going with it in this PR.
#### Alternative: The Type Registry Approach
The `Proxy` trait is great and all, but how does it solve the original
problem? Well, it doesn't— yet!
The goal would be to start moving information from the derive macro and
its attributes to the generated `TypeInfo` since these are known
statically and shouldn't change. For example, adding `ignored: bool` to
`[Un]NamedField` or a list of impls.
However, there is another way of storing this information. This is, of
course, one of the uses of the `TypeRegistry`. If we're worried about
Dynamic proxies not aligning with their concrete counterparts, we could
move more type information to the registry and require its usage.
For example, we could replace `Reflect::reflect_hash(&self)` with
`Reflect::reflect_hash(&self, registry: &TypeRegistry)`.
That's not the _worst_ thing in the world, but it is an ergonomics loss.
Additionally, other attributes may have their own requirements, further
restricting what's possible without the registry. The `Reflect::apply`
method will require the registry as well now. Why? Well because the
`map_apply` function used for the `Reflect::apply` impls on `Map` types
depends on `Map::insert_boxed`, which (at least for `DynamicMap`)
requires `Reflect::reflect_hash`. The same would apply when adding
support for reflection-based diffing, which will require
`Reflect::reflect_partial_eq`.
Again, this is a totally viable alternative. I just chose not to go with
it for the reasons above. If we want to go with it, then we can close
this PR and we can pursue this alternative instead.
#### Downsides
Just to highlight a quick potential downside (likely needs more
investigation): retrieving the `TypeInfo` requires acquiring a lock on
the `GenericTypeInfoCell` used by the `Typed` impls for generic types
(non-generic types use a `OnceBox which should be faster). I am not sure
how much of a performance hit that is and will need to run some
benchmarks to compare against.
</details>
### Open Questions
1. Should we use `Cow<'static, TypeInfo>` instead? I think that might be
easier for modding? Perhaps, in that case, we need to update
`Typed::type_info` and friends as well?
2. Are the alternatives better than the approach this PR takes? Are
there other alternatives?
---
## Changelog
### Changed
- `Reflect::get_type_info` has been renamed to
`Reflect::represented_type_info`
- This method now returns `Option<&'static TypeInfo>` rather than just
`&'static TypeInfo`
### Added
- Added `Reflect::is_dynamic` method to indicate when a type is dynamic
- Added a `set_represented_type` method on all dynamic types
### Removed
- Removed `TypeInfo::Dynamic` (use `Reflect::is_dynamic` instead)
- Removed `Typed` impls for all dynamic types
## Migration Guide
- The Dynamic types no longer take a string type name. Instead, they
require a static reference to `TypeInfo`:
```rust
#[derive(Reflect)]
struct MyTupleStruct(f32, f32);
let mut dyn_tuple_struct = DynamicTupleStruct::default();
dyn_tuple_struct.insert(1.23_f32);
dyn_tuple_struct.insert(3.21_f32);
// BEFORE:
let type_name = std::any::type_name::<MyTupleStruct>();
dyn_tuple_struct.set_name(type_name);
// AFTER:
let type_info = <MyTupleStruct as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
```
- `Reflect::get_type_info` has been renamed to
`Reflect::represented_type_info` and now also returns an
`Option<&'static TypeInfo>` (instead of just `&'static TypeInfo`):
```rust
// BEFORE:
let info: &'static TypeInfo = value.get_type_info();
// AFTER:
let info: &'static TypeInfo = value.represented_type_info().unwrap();
```
- `TypeInfo::Dynamic` and `DynamicInfo` has been removed. Use
`Reflect::is_dynamic` instead:
```rust
// BEFORE:
if matches!(value.get_type_info(), TypeInfo::Dynamic) {
// ...
}
// AFTER:
if value.is_dynamic() {
// ...
}
```
---------
Co-authored-by: radiish <cb.setho@gmail.com>
2023-04-26 12:17:46 +00:00
|
|
|
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
|
|
|
Some(<Self as Typed>::type_info())
|
2023-02-27 21:37:36 +00:00
|
|
|
}
|
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
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
fn into_any(self: Box<Self>) -> Box<dyn Any> {
|
|
|
|
self
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
fn as_any(&self) -> &dyn Any {
|
|
|
|
self
|
|
|
|
}
|
Make `Reflect` safe to implement (#5010)
# Objective
Currently, `Reflect` is unsafe to implement because of a contract in which `any` and `any_mut` must return `self`, or `downcast` will cause UB. This PR makes `Reflect` safe, makes `downcast` not use unsafe, and eliminates this contract.
## Solution
This PR adds a method to `Reflect`, `any`. It also renames the old `any` to `as_any`.
`any` now takes a `Box<Self>` and returns a `Box<dyn Any>`.
---
## Changelog
### Added:
- `any()` method
- `represents()` method
### Changed:
- `Reflect` is now a safe trait
- `downcast()` is now safe
- The old `any` is now called `as_any`, and `any_mut` is now `as_mut_any`
## Migration Guide
- Reflect derives should not have to change anything
- Manual reflect impls will need to remove the `unsafe` keyword, add `any()` implementations, and rename the old `any` and `any_mut` to `as_any` and `as_mut_any`.
- Calls to `any`/`any_mut` must be changed to `as_any`/`as_mut_any`
## Points of discussion:
- Should renaming `any` be avoided and instead name the new method `any_box`?
- ~~Could there be a performance regression from avoiding the unsafe? I doubt it, but this change does seem to introduce redundant checks.~~
- ~~Could/should `is` and `type_id()` be implemented differently? For example, moving `is` onto `Reflect` as an `fn(&self, TypeId) -> bool`~~
Co-authored-by: PROMETHIA-27 <42193387+PROMETHIA-27@users.noreply.github.com>
2022-06-27 16:52:25 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
|
|
|
self
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
#[inline]
|
|
|
|
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
|
|
|
|
self
|
|
|
|
}
|
2022-11-07 02:11:16 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
fn as_reflect(&self) -> &dyn Reflect {
|
|
|
|
self
|
|
|
|
}
|
2022-04-25 13:54:48 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
|
|
|
|
self
|
|
|
|
}
|
2022-04-25 13:54:48 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
fn apply(&mut self, value: &dyn Reflect) {
|
|
|
|
map_apply(self, value);
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2023-02-27 21:37:36 +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
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
fn reflect_ref(&self) -> ReflectRef {
|
|
|
|
ReflectRef::Map(self)
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
fn reflect_mut(&mut self) -> ReflectMut {
|
|
|
|
ReflectMut::Map(self)
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
|
|
|
|
ReflectOwned::Map(self)
|
|
|
|
}
|
2022-11-06 16:58:38 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
fn clone_value(&self) -> Box<dyn Reflect> {
|
|
|
|
Box::new(self.clone_dynamic())
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
|
|
|
|
map_partial_eq(self, value)
|
|
|
|
}
|
|
|
|
}
|
2021-02-01 00:35:23 +00:00
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
impl<K, V, S> Typed for $ty
|
|
|
|
where
|
|
|
|
K: FromReflect + Eq + Hash,
|
|
|
|
V: FromReflect,
|
|
|
|
S: BuildHasher + Send + Sync + 'static,
|
|
|
|
{
|
|
|
|
fn type_info() -> &'static TypeInfo {
|
|
|
|
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
|
|
|
|
CELL.get_or_insert::<Self, _>(|| TypeInfo::Map(MapInfo::new::<Self, 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
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
impl<K, V, S> GetTypeRegistration for $ty
|
|
|
|
where
|
|
|
|
K: FromReflect + Eq + Hash,
|
|
|
|
V: FromReflect,
|
|
|
|
S: BuildHasher + Send + Sync + 'static,
|
|
|
|
{
|
|
|
|
fn get_type_registration() -> TypeRegistration {
|
|
|
|
let mut registration = TypeRegistration::of::<Self>();
|
|
|
|
registration.insert::<ReflectFromPtr>(FromType::<Self>::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
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
impl<K, V, S> FromReflect for $ty
|
|
|
|
where
|
|
|
|
K: FromReflect + Eq + Hash,
|
|
|
|
V: FromReflect,
|
|
|
|
S: BuildHasher + Default + Send + Sync + 'static,
|
|
|
|
{
|
|
|
|
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
|
|
|
|
if let ReflectRef::Map(ref_map) = reflect.reflect_ref() {
|
|
|
|
let mut new_map = Self::with_capacity_and_hasher(ref_map.len(), S::default());
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
}
|
2023-02-27 21:37:36 +00:00
|
|
|
};
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
}
|
|
|
|
|
2023-02-27 21:37:36 +00:00
|
|
|
impl_reflect_for_hashmap!(bevy_utils::hashbrown::HashMap<K, V, S>);
|
|
|
|
impl_reflect_for_hashmap!(std::collections::HashMap<K, V, S>);
|
|
|
|
|
2022-05-13 01:13:30 +00:00
|
|
|
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 {
|
2023-01-31 23:19:19 +00:00
|
|
|
ArrayIter::new(self)
|
2022-05-13 01:13:30 +00:00
|
|
|
}
|
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()
|
|
|
|
}
|
2022-05-13 01:13:30 +00:00
|
|
|
}
|
|
|
|
|
Make `Reflect` safe to implement (#5010)
# Objective
Currently, `Reflect` is unsafe to implement because of a contract in which `any` and `any_mut` must return `self`, or `downcast` will cause UB. This PR makes `Reflect` safe, makes `downcast` not use unsafe, and eliminates this contract.
## Solution
This PR adds a method to `Reflect`, `any`. It also renames the old `any` to `as_any`.
`any` now takes a `Box<Self>` and returns a `Box<dyn Any>`.
---
## Changelog
### Added:
- `any()` method
- `represents()` method
### Changed:
- `Reflect` is now a safe trait
- `downcast()` is now safe
- The old `any` is now called `as_any`, and `any_mut` is now `as_mut_any`
## Migration Guide
- Reflect derives should not have to change anything
- Manual reflect impls will need to remove the `unsafe` keyword, add `any()` implementations, and rename the old `any` and `any_mut` to `as_any` and `as_mut_any`.
- Calls to `any`/`any_mut` must be changed to `as_any`/`as_mut_any`
## Points of discussion:
- Should renaming `any` be avoided and instead name the new method `any_box`?
- ~~Could there be a performance regression from avoiding the unsafe? I doubt it, but this change does seem to introduce redundant checks.~~
- ~~Could/should `is` and `type_id()` be implemented differently? For example, moving `is` onto `Reflect` as an `fn(&self, TypeId) -> bool`~~
Co-authored-by: PROMETHIA-27 <42193387+PROMETHIA-27@users.noreply.github.com>
2022-06-27 16:52:25 +00:00
|
|
|
impl<T: Reflect, const N: usize> Reflect for [T; N] {
|
2022-05-13 01:13:30 +00:00
|
|
|
#[inline]
|
|
|
|
fn type_name(&self) -> &str {
|
|
|
|
std::any::type_name::<Self>()
|
|
|
|
}
|
|
|
|
|
bevy_reflect: Better proxies (#6971)
# Objective
> This PR is based on discussion from #6601
The Dynamic types (e.g. `DynamicStruct`, `DynamicList`, etc.) act as
both:
1. Dynamic containers which may hold any arbitrary data
2. Proxy types which may represent any other type
Currently, the only way we can represent the proxy-ness of a Dynamic is
by giving it a name.
```rust
// This is just a dynamic container
let mut data = DynamicStruct::default();
// This is a "proxy"
data.set_name(std::any::type_name::<Foo>());
```
This type name is the only way we check that the given Dynamic is a
proxy of some other type. When we need to "assert the type" of a `dyn
Reflect`, we call `Reflect::type_name` on it. However, because we're
only using a string to denote the type, we run into a few gotchas and
limitations.
For example, hashing a Dynamic proxy may work differently than the type
it proxies:
```rust
#[derive(Reflect, Hash)]
#[reflect(Hash)]
struct Foo(i32);
let concrete = Foo(123);
let dynamic = concrete.clone_dynamic();
let concrete_hash = concrete.reflect_hash();
let dynamic_hash = dynamic.reflect_hash();
// The hashes are not equal because `concrete` uses its own `Hash` impl
// while `dynamic` uses a reflection-based hashing algorithm
assert_ne!(concrete_hash, dynamic_hash);
```
Because the Dynamic proxy only knows about the name of the type, it's
unaware of any other information about it. This means it also differs on
`Reflect::reflect_partial_eq`, and may include ignored or skipped fields
in places the concrete type wouldn't.
## Solution
Rather than having Dynamics pass along just the type name of proxied
types, we can instead have them pass around the `TypeInfo`.
Now all Dynamic types contain an `Option<&'static TypeInfo>` rather than
a `String`:
```diff
pub struct DynamicTupleStruct {
- type_name: String,
+ represented_type: Option<&'static TypeInfo>,
fields: Vec<Box<dyn Reflect>>,
}
```
By changing `Reflect::get_type_info` to
`Reflect::represented_type_info`, hopefully we make this behavior a
little clearer. And to account for `None` values on these dynamic types,
`Reflect::represented_type_info` now returns `Option<&'static
TypeInfo>`.
```rust
let mut data = DynamicTupleStruct::default();
// Not proxying any specific type
assert!(dyn_tuple_struct.represented_type_info().is_none());
let type_info = <Foo as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
// Alternatively:
// let dyn_tuple_struct = foo.clone_dynamic();
// Now we're proxying `Foo`
assert!(dyn_tuple_struct.represented_type_info().is_some());
```
This means that we can have full access to all the static type
information for the proxied type. Future work would include
transitioning more static type information (trait impls, attributes,
etc.) over to the `TypeInfo` so it can actually be utilized by Dynamic
proxies.
### Alternatives & Rationale
> **Note**
> These alternatives were written when this PR was first made using a
`Proxy` trait. This trait has since been removed.
<details>
<summary>View</summary>
#### Alternative: The `Proxy<T>` Approach
I had considered adding something like a `Proxy<T>` type where `T` would
be the Dynamic and would contain the proxied type information.
This was nice in that it allows us to explicitly determine whether
something is a proxy or not at a type level. `Proxy<DynamicStruct>`
proxies a struct. Makes sense.
The reason I didn't go with this approach is because (1) tuples, (2)
complexity, and (3) `PartialReflect`.
The `DynamicTuple` struct allows us to represent tuples at runtime. It
also allows us to do something you normally can't with tuples: add new
fields. Because of this, adding a field immediately invalidates the
proxy (e.g. our info for `(i32, i32)` doesn't apply to `(i32, i32,
NewField)`). By going with this PR's approach, we can just remove the
type info on `DynamicTuple` when that happens. However, with the
`Proxy<T>` approach, it becomes difficult to represent this behavior—
we'd have to completely control how we access data for `T` for each `T`.
Secondly, it introduces some added complexities (aside from the manual
impls for each `T`). Does `Proxy<T>` impl `Reflect`? Likely yes, if we
want to represent it as `dyn Reflect`. What `TypeInfo` do we give it?
How would we forward reflection methods to the inner type (remember, we
don't have specialization)? How do we separate this from Dynamic types?
And finally, how do all this in a way that's both logical and intuitive
for users?
Lastly, introducing a `Proxy` trait rather than a `Proxy<T>` struct is
actually more inline with the [Unique Reflect
RFC](https://github.com/bevyengine/rfcs/pull/56). In a way, the `Proxy`
trait is really one part of the `PartialReflect` trait introduced in
that RFC (it's technically not in that RFC but it fits well with it),
where the `PartialReflect` serves as a way for proxies to work _like_
concrete types without having full access to everything a concrete
`Reflect` type can do. This would help bridge the gap between the
current state of the crate and the implementation of that RFC.
All that said, this is still a viable solution. If the community
believes this is the better path forward, then we can do that instead.
These were just my reasons for not initially going with it in this PR.
#### Alternative: The Type Registry Approach
The `Proxy` trait is great and all, but how does it solve the original
problem? Well, it doesn't— yet!
The goal would be to start moving information from the derive macro and
its attributes to the generated `TypeInfo` since these are known
statically and shouldn't change. For example, adding `ignored: bool` to
`[Un]NamedField` or a list of impls.
However, there is another way of storing this information. This is, of
course, one of the uses of the `TypeRegistry`. If we're worried about
Dynamic proxies not aligning with their concrete counterparts, we could
move more type information to the registry and require its usage.
For example, we could replace `Reflect::reflect_hash(&self)` with
`Reflect::reflect_hash(&self, registry: &TypeRegistry)`.
That's not the _worst_ thing in the world, but it is an ergonomics loss.
Additionally, other attributes may have their own requirements, further
restricting what's possible without the registry. The `Reflect::apply`
method will require the registry as well now. Why? Well because the
`map_apply` function used for the `Reflect::apply` impls on `Map` types
depends on `Map::insert_boxed`, which (at least for `DynamicMap`)
requires `Reflect::reflect_hash`. The same would apply when adding
support for reflection-based diffing, which will require
`Reflect::reflect_partial_eq`.
Again, this is a totally viable alternative. I just chose not to go with
it for the reasons above. If we want to go with it, then we can close
this PR and we can pursue this alternative instead.
#### Downsides
Just to highlight a quick potential downside (likely needs more
investigation): retrieving the `TypeInfo` requires acquiring a lock on
the `GenericTypeInfoCell` used by the `Typed` impls for generic types
(non-generic types use a `OnceBox which should be faster). I am not sure
how much of a performance hit that is and will need to run some
benchmarks to compare against.
</details>
### Open Questions
1. Should we use `Cow<'static, TypeInfo>` instead? I think that might be
easier for modding? Perhaps, in that case, we need to update
`Typed::type_info` and friends as well?
2. Are the alternatives better than the approach this PR takes? Are
there other alternatives?
---
## Changelog
### Changed
- `Reflect::get_type_info` has been renamed to
`Reflect::represented_type_info`
- This method now returns `Option<&'static TypeInfo>` rather than just
`&'static TypeInfo`
### Added
- Added `Reflect::is_dynamic` method to indicate when a type is dynamic
- Added a `set_represented_type` method on all dynamic types
### Removed
- Removed `TypeInfo::Dynamic` (use `Reflect::is_dynamic` instead)
- Removed `Typed` impls for all dynamic types
## Migration Guide
- The Dynamic types no longer take a string type name. Instead, they
require a static reference to `TypeInfo`:
```rust
#[derive(Reflect)]
struct MyTupleStruct(f32, f32);
let mut dyn_tuple_struct = DynamicTupleStruct::default();
dyn_tuple_struct.insert(1.23_f32);
dyn_tuple_struct.insert(3.21_f32);
// BEFORE:
let type_name = std::any::type_name::<MyTupleStruct>();
dyn_tuple_struct.set_name(type_name);
// AFTER:
let type_info = <MyTupleStruct as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
```
- `Reflect::get_type_info` has been renamed to
`Reflect::represented_type_info` and now also returns an
`Option<&'static TypeInfo>` (instead of just `&'static TypeInfo`):
```rust
// BEFORE:
let info: &'static TypeInfo = value.get_type_info();
// AFTER:
let info: &'static TypeInfo = value.represented_type_info().unwrap();
```
- `TypeInfo::Dynamic` and `DynamicInfo` has been removed. Use
`Reflect::is_dynamic` instead:
```rust
// BEFORE:
if matches!(value.get_type_info(), TypeInfo::Dynamic) {
// ...
}
// AFTER:
if value.is_dynamic() {
// ...
}
```
---------
Co-authored-by: radiish <cb.setho@gmail.com>
2023-04-26 12:17:46 +00:00
|
|
|
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
|
|
|
Some(<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
|
|
|
}
|
|
|
|
|
2022-05-13 01:13:30 +00:00
|
|
|
#[inline]
|
Make `Reflect` safe to implement (#5010)
# Objective
Currently, `Reflect` is unsafe to implement because of a contract in which `any` and `any_mut` must return `self`, or `downcast` will cause UB. This PR makes `Reflect` safe, makes `downcast` not use unsafe, and eliminates this contract.
## Solution
This PR adds a method to `Reflect`, `any`. It also renames the old `any` to `as_any`.
`any` now takes a `Box<Self>` and returns a `Box<dyn Any>`.
---
## Changelog
### Added:
- `any()` method
- `represents()` method
### Changed:
- `Reflect` is now a safe trait
- `downcast()` is now safe
- The old `any` is now called `as_any`, and `any_mut` is now `as_mut_any`
## Migration Guide
- Reflect derives should not have to change anything
- Manual reflect impls will need to remove the `unsafe` keyword, add `any()` implementations, and rename the old `any` and `any_mut` to `as_any` and `as_mut_any`.
- Calls to `any`/`any_mut` must be changed to `as_any`/`as_mut_any`
## Points of discussion:
- Should renaming `any` be avoided and instead name the new method `any_box`?
- ~~Could there be a performance regression from avoiding the unsafe? I doubt it, but this change does seem to introduce redundant checks.~~
- ~~Could/should `is` and `type_id()` be implemented differently? For example, moving `is` onto `Reflect` as an `fn(&self, TypeId) -> bool`~~
Co-authored-by: PROMETHIA-27 <42193387+PROMETHIA-27@users.noreply.github.com>
2022-06-27 16:52:25 +00:00
|
|
|
fn into_any(self: Box<Self>) -> Box<dyn Any> {
|
2022-05-13 01:13:30 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
Make `Reflect` safe to implement (#5010)
# Objective
Currently, `Reflect` is unsafe to implement because of a contract in which `any` and `any_mut` must return `self`, or `downcast` will cause UB. This PR makes `Reflect` safe, makes `downcast` not use unsafe, and eliminates this contract.
## Solution
This PR adds a method to `Reflect`, `any`. It also renames the old `any` to `as_any`.
`any` now takes a `Box<Self>` and returns a `Box<dyn Any>`.
---
## Changelog
### Added:
- `any()` method
- `represents()` method
### Changed:
- `Reflect` is now a safe trait
- `downcast()` is now safe
- The old `any` is now called `as_any`, and `any_mut` is now `as_mut_any`
## Migration Guide
- Reflect derives should not have to change anything
- Manual reflect impls will need to remove the `unsafe` keyword, add `any()` implementations, and rename the old `any` and `any_mut` to `as_any` and `as_mut_any`.
- Calls to `any`/`any_mut` must be changed to `as_any`/`as_mut_any`
## Points of discussion:
- Should renaming `any` be avoided and instead name the new method `any_box`?
- ~~Could there be a performance regression from avoiding the unsafe? I doubt it, but this change does seem to introduce redundant checks.~~
- ~~Could/should `is` and `type_id()` be implemented differently? For example, moving `is` onto `Reflect` as an `fn(&self, TypeId) -> bool`~~
Co-authored-by: PROMETHIA-27 <42193387+PROMETHIA-27@users.noreply.github.com>
2022-06-27 16:52:25 +00:00
|
|
|
fn as_any(&self) -> &dyn Any {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
2022-05-13 01:13:30 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2022-11-07 02:11:16 +00:00
|
|
|
#[inline]
|
|
|
|
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2022-05-13 01:13:30 +00:00
|
|
|
#[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)
|
|
|
|
}
|
|
|
|
|
2022-11-06 16:58:38 +00:00
|
|
|
#[inline]
|
|
|
|
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
|
|
|
|
ReflectOwned::Array(self)
|
|
|
|
}
|
|
|
|
|
2022-05-13 01:13:30 +00:00
|
|
|
#[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)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-13 01:13:30 +00:00
|
|
|
// 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] {
|
2022-05-13 01:13:30 +00:00
|
|
|
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]>()
|
2022-05-13 01:13:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
)+
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-08-17 00:21:15 +00:00
|
|
|
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>>()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-17 00:21:15 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-17 00:21:15 +00:00
|
|
|
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]
|
bevy_reflect: Better proxies (#6971)
# Objective
> This PR is based on discussion from #6601
The Dynamic types (e.g. `DynamicStruct`, `DynamicList`, etc.) act as
both:
1. Dynamic containers which may hold any arbitrary data
2. Proxy types which may represent any other type
Currently, the only way we can represent the proxy-ness of a Dynamic is
by giving it a name.
```rust
// This is just a dynamic container
let mut data = DynamicStruct::default();
// This is a "proxy"
data.set_name(std::any::type_name::<Foo>());
```
This type name is the only way we check that the given Dynamic is a
proxy of some other type. When we need to "assert the type" of a `dyn
Reflect`, we call `Reflect::type_name` on it. However, because we're
only using a string to denote the type, we run into a few gotchas and
limitations.
For example, hashing a Dynamic proxy may work differently than the type
it proxies:
```rust
#[derive(Reflect, Hash)]
#[reflect(Hash)]
struct Foo(i32);
let concrete = Foo(123);
let dynamic = concrete.clone_dynamic();
let concrete_hash = concrete.reflect_hash();
let dynamic_hash = dynamic.reflect_hash();
// The hashes are not equal because `concrete` uses its own `Hash` impl
// while `dynamic` uses a reflection-based hashing algorithm
assert_ne!(concrete_hash, dynamic_hash);
```
Because the Dynamic proxy only knows about the name of the type, it's
unaware of any other information about it. This means it also differs on
`Reflect::reflect_partial_eq`, and may include ignored or skipped fields
in places the concrete type wouldn't.
## Solution
Rather than having Dynamics pass along just the type name of proxied
types, we can instead have them pass around the `TypeInfo`.
Now all Dynamic types contain an `Option<&'static TypeInfo>` rather than
a `String`:
```diff
pub struct DynamicTupleStruct {
- type_name: String,
+ represented_type: Option<&'static TypeInfo>,
fields: Vec<Box<dyn Reflect>>,
}
```
By changing `Reflect::get_type_info` to
`Reflect::represented_type_info`, hopefully we make this behavior a
little clearer. And to account for `None` values on these dynamic types,
`Reflect::represented_type_info` now returns `Option<&'static
TypeInfo>`.
```rust
let mut data = DynamicTupleStruct::default();
// Not proxying any specific type
assert!(dyn_tuple_struct.represented_type_info().is_none());
let type_info = <Foo as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
// Alternatively:
// let dyn_tuple_struct = foo.clone_dynamic();
// Now we're proxying `Foo`
assert!(dyn_tuple_struct.represented_type_info().is_some());
```
This means that we can have full access to all the static type
information for the proxied type. Future work would include
transitioning more static type information (trait impls, attributes,
etc.) over to the `TypeInfo` so it can actually be utilized by Dynamic
proxies.
### Alternatives & Rationale
> **Note**
> These alternatives were written when this PR was first made using a
`Proxy` trait. This trait has since been removed.
<details>
<summary>View</summary>
#### Alternative: The `Proxy<T>` Approach
I had considered adding something like a `Proxy<T>` type where `T` would
be the Dynamic and would contain the proxied type information.
This was nice in that it allows us to explicitly determine whether
something is a proxy or not at a type level. `Proxy<DynamicStruct>`
proxies a struct. Makes sense.
The reason I didn't go with this approach is because (1) tuples, (2)
complexity, and (3) `PartialReflect`.
The `DynamicTuple` struct allows us to represent tuples at runtime. It
also allows us to do something you normally can't with tuples: add new
fields. Because of this, adding a field immediately invalidates the
proxy (e.g. our info for `(i32, i32)` doesn't apply to `(i32, i32,
NewField)`). By going with this PR's approach, we can just remove the
type info on `DynamicTuple` when that happens. However, with the
`Proxy<T>` approach, it becomes difficult to represent this behavior—
we'd have to completely control how we access data for `T` for each `T`.
Secondly, it introduces some added complexities (aside from the manual
impls for each `T`). Does `Proxy<T>` impl `Reflect`? Likely yes, if we
want to represent it as `dyn Reflect`. What `TypeInfo` do we give it?
How would we forward reflection methods to the inner type (remember, we
don't have specialization)? How do we separate this from Dynamic types?
And finally, how do all this in a way that's both logical and intuitive
for users?
Lastly, introducing a `Proxy` trait rather than a `Proxy<T>` struct is
actually more inline with the [Unique Reflect
RFC](https://github.com/bevyengine/rfcs/pull/56). In a way, the `Proxy`
trait is really one part of the `PartialReflect` trait introduced in
that RFC (it's technically not in that RFC but it fits well with it),
where the `PartialReflect` serves as a way for proxies to work _like_
concrete types without having full access to everything a concrete
`Reflect` type can do. This would help bridge the gap between the
current state of the crate and the implementation of that RFC.
All that said, this is still a viable solution. If the community
believes this is the better path forward, then we can do that instead.
These were just my reasons for not initially going with it in this PR.
#### Alternative: The Type Registry Approach
The `Proxy` trait is great and all, but how does it solve the original
problem? Well, it doesn't— yet!
The goal would be to start moving information from the derive macro and
its attributes to the generated `TypeInfo` since these are known
statically and shouldn't change. For example, adding `ignored: bool` to
`[Un]NamedField` or a list of impls.
However, there is another way of storing this information. This is, of
course, one of the uses of the `TypeRegistry`. If we're worried about
Dynamic proxies not aligning with their concrete counterparts, we could
move more type information to the registry and require its usage.
For example, we could replace `Reflect::reflect_hash(&self)` with
`Reflect::reflect_hash(&self, registry: &TypeRegistry)`.
That's not the _worst_ thing in the world, but it is an ergonomics loss.
Additionally, other attributes may have their own requirements, further
restricting what's possible without the registry. The `Reflect::apply`
method will require the registry as well now. Why? Well because the
`map_apply` function used for the `Reflect::apply` impls on `Map` types
depends on `Map::insert_boxed`, which (at least for `DynamicMap`)
requires `Reflect::reflect_hash`. The same would apply when adding
support for reflection-based diffing, which will require
`Reflect::reflect_partial_eq`.
Again, this is a totally viable alternative. I just chose not to go with
it for the reasons above. If we want to go with it, then we can close
this PR and we can pursue this alternative instead.
#### Downsides
Just to highlight a quick potential downside (likely needs more
investigation): retrieving the `TypeInfo` requires acquiring a lock on
the `GenericTypeInfoCell` used by the `Typed` impls for generic types
(non-generic types use a `OnceBox which should be faster). I am not sure
how much of a performance hit that is and will need to run some
benchmarks to compare against.
</details>
### Open Questions
1. Should we use `Cow<'static, TypeInfo>` instead? I think that might be
easier for modding? Perhaps, in that case, we need to update
`Typed::type_info` and friends as well?
2. Are the alternatives better than the approach this PR takes? Are
there other alternatives?
---
## Changelog
### Changed
- `Reflect::get_type_info` has been renamed to
`Reflect::represented_type_info`
- This method now returns `Option<&'static TypeInfo>` rather than just
`&'static TypeInfo`
### Added
- Added `Reflect::is_dynamic` method to indicate when a type is dynamic
- Added a `set_represented_type` method on all dynamic types
### Removed
- Removed `TypeInfo::Dynamic` (use `Reflect::is_dynamic` instead)
- Removed `Typed` impls for all dynamic types
## Migration Guide
- The Dynamic types no longer take a string type name. Instead, they
require a static reference to `TypeInfo`:
```rust
#[derive(Reflect)]
struct MyTupleStruct(f32, f32);
let mut dyn_tuple_struct = DynamicTupleStruct::default();
dyn_tuple_struct.insert(1.23_f32);
dyn_tuple_struct.insert(3.21_f32);
// BEFORE:
let type_name = std::any::type_name::<MyTupleStruct>();
dyn_tuple_struct.set_name(type_name);
// AFTER:
let type_info = <MyTupleStruct as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
```
- `Reflect::get_type_info` has been renamed to
`Reflect::represented_type_info` and now also returns an
`Option<&'static TypeInfo>` (instead of just `&'static TypeInfo`):
```rust
// BEFORE:
let info: &'static TypeInfo = value.get_type_info();
// AFTER:
let info: &'static TypeInfo = value.represented_type_info().unwrap();
```
- `TypeInfo::Dynamic` and `DynamicInfo` has been removed. Use
`Reflect::is_dynamic` instead:
```rust
// BEFORE:
if matches!(value.get_type_info(), TypeInfo::Dynamic) {
// ...
}
// AFTER:
if value.is_dynamic() {
// ...
}
```
---------
Co-authored-by: radiish <cb.setho@gmail.com>
2023-04-26 12:17:46 +00:00
|
|
|
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
|
|
|
Some(<Self as Typed>::type_info())
|
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 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
|
|
|
|
}
|
|
|
|
|
2022-11-07 02:11:16 +00:00
|
|
|
#[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() {
|
2022-08-24 20:44:35 +00:00
|
|
|
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" => {
|
2023-01-11 16:25:37 +00:00
|
|
|
let field = T::take_from_reflect(
|
|
|
|
value
|
|
|
|
.field_at(0)
|
|
|
|
.unwrap_or_else(|| {
|
2022-08-24 20:44:35 +00:00
|
|
|
panic!(
|
2023-01-11 16:25:37 +00:00
|
|
|
"Field in `Some` variant of {} should exist",
|
|
|
|
std::any::type_name::<Option<T>>()
|
2022-08-24 20:44:35 +00:00
|
|
|
)
|
|
|
|
})
|
2023-01-11 16:25:37 +00:00
|
|
|
.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)
|
|
|
|
}
|
|
|
|
|
2022-11-06 16:58:38 +00:00
|
|
|
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> {
|
2022-08-17 00:21:15 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-17 00:21:15 +00:00
|
|
|
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" => {
|
2023-01-11 16:25:37 +00:00
|
|
|
let field = T::take_from_reflect(
|
|
|
|
dyn_enum
|
|
|
|
.field_at(0)
|
|
|
|
.unwrap_or_else(|| {
|
2022-08-24 20:44:35 +00:00
|
|
|
panic!(
|
2023-01-11 16:25:37 +00:00
|
|
|
"Field in `Some` variant of {} should exist",
|
|
|
|
std::any::type_name::<Option<T>>()
|
2022-08-24 20:44:35 +00:00
|
|
|
)
|
|
|
|
})
|
2023-01-11 16:25:37 +00:00
|
|
|
.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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-17 00:21:15 +00:00
|
|
|
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
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-03 03:35:45 +00:00
|
|
|
impl Reflect for Cow<'static, str> {
|
|
|
|
fn type_name(&self) -> &str {
|
|
|
|
std::any::type_name::<Self>()
|
|
|
|
}
|
|
|
|
|
bevy_reflect: Better proxies (#6971)
# Objective
> This PR is based on discussion from #6601
The Dynamic types (e.g. `DynamicStruct`, `DynamicList`, etc.) act as
both:
1. Dynamic containers which may hold any arbitrary data
2. Proxy types which may represent any other type
Currently, the only way we can represent the proxy-ness of a Dynamic is
by giving it a name.
```rust
// This is just a dynamic container
let mut data = DynamicStruct::default();
// This is a "proxy"
data.set_name(std::any::type_name::<Foo>());
```
This type name is the only way we check that the given Dynamic is a
proxy of some other type. When we need to "assert the type" of a `dyn
Reflect`, we call `Reflect::type_name` on it. However, because we're
only using a string to denote the type, we run into a few gotchas and
limitations.
For example, hashing a Dynamic proxy may work differently than the type
it proxies:
```rust
#[derive(Reflect, Hash)]
#[reflect(Hash)]
struct Foo(i32);
let concrete = Foo(123);
let dynamic = concrete.clone_dynamic();
let concrete_hash = concrete.reflect_hash();
let dynamic_hash = dynamic.reflect_hash();
// The hashes are not equal because `concrete` uses its own `Hash` impl
// while `dynamic` uses a reflection-based hashing algorithm
assert_ne!(concrete_hash, dynamic_hash);
```
Because the Dynamic proxy only knows about the name of the type, it's
unaware of any other information about it. This means it also differs on
`Reflect::reflect_partial_eq`, and may include ignored or skipped fields
in places the concrete type wouldn't.
## Solution
Rather than having Dynamics pass along just the type name of proxied
types, we can instead have them pass around the `TypeInfo`.
Now all Dynamic types contain an `Option<&'static TypeInfo>` rather than
a `String`:
```diff
pub struct DynamicTupleStruct {
- type_name: String,
+ represented_type: Option<&'static TypeInfo>,
fields: Vec<Box<dyn Reflect>>,
}
```
By changing `Reflect::get_type_info` to
`Reflect::represented_type_info`, hopefully we make this behavior a
little clearer. And to account for `None` values on these dynamic types,
`Reflect::represented_type_info` now returns `Option<&'static
TypeInfo>`.
```rust
let mut data = DynamicTupleStruct::default();
// Not proxying any specific type
assert!(dyn_tuple_struct.represented_type_info().is_none());
let type_info = <Foo as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
// Alternatively:
// let dyn_tuple_struct = foo.clone_dynamic();
// Now we're proxying `Foo`
assert!(dyn_tuple_struct.represented_type_info().is_some());
```
This means that we can have full access to all the static type
information for the proxied type. Future work would include
transitioning more static type information (trait impls, attributes,
etc.) over to the `TypeInfo` so it can actually be utilized by Dynamic
proxies.
### Alternatives & Rationale
> **Note**
> These alternatives were written when this PR was first made using a
`Proxy` trait. This trait has since been removed.
<details>
<summary>View</summary>
#### Alternative: The `Proxy<T>` Approach
I had considered adding something like a `Proxy<T>` type where `T` would
be the Dynamic and would contain the proxied type information.
This was nice in that it allows us to explicitly determine whether
something is a proxy or not at a type level. `Proxy<DynamicStruct>`
proxies a struct. Makes sense.
The reason I didn't go with this approach is because (1) tuples, (2)
complexity, and (3) `PartialReflect`.
The `DynamicTuple` struct allows us to represent tuples at runtime. It
also allows us to do something you normally can't with tuples: add new
fields. Because of this, adding a field immediately invalidates the
proxy (e.g. our info for `(i32, i32)` doesn't apply to `(i32, i32,
NewField)`). By going with this PR's approach, we can just remove the
type info on `DynamicTuple` when that happens. However, with the
`Proxy<T>` approach, it becomes difficult to represent this behavior—
we'd have to completely control how we access data for `T` for each `T`.
Secondly, it introduces some added complexities (aside from the manual
impls for each `T`). Does `Proxy<T>` impl `Reflect`? Likely yes, if we
want to represent it as `dyn Reflect`. What `TypeInfo` do we give it?
How would we forward reflection methods to the inner type (remember, we
don't have specialization)? How do we separate this from Dynamic types?
And finally, how do all this in a way that's both logical and intuitive
for users?
Lastly, introducing a `Proxy` trait rather than a `Proxy<T>` struct is
actually more inline with the [Unique Reflect
RFC](https://github.com/bevyengine/rfcs/pull/56). In a way, the `Proxy`
trait is really one part of the `PartialReflect` trait introduced in
that RFC (it's technically not in that RFC but it fits well with it),
where the `PartialReflect` serves as a way for proxies to work _like_
concrete types without having full access to everything a concrete
`Reflect` type can do. This would help bridge the gap between the
current state of the crate and the implementation of that RFC.
All that said, this is still a viable solution. If the community
believes this is the better path forward, then we can do that instead.
These were just my reasons for not initially going with it in this PR.
#### Alternative: The Type Registry Approach
The `Proxy` trait is great and all, but how does it solve the original
problem? Well, it doesn't— yet!
The goal would be to start moving information from the derive macro and
its attributes to the generated `TypeInfo` since these are known
statically and shouldn't change. For example, adding `ignored: bool` to
`[Un]NamedField` or a list of impls.
However, there is another way of storing this information. This is, of
course, one of the uses of the `TypeRegistry`. If we're worried about
Dynamic proxies not aligning with their concrete counterparts, we could
move more type information to the registry and require its usage.
For example, we could replace `Reflect::reflect_hash(&self)` with
`Reflect::reflect_hash(&self, registry: &TypeRegistry)`.
That's not the _worst_ thing in the world, but it is an ergonomics loss.
Additionally, other attributes may have their own requirements, further
restricting what's possible without the registry. The `Reflect::apply`
method will require the registry as well now. Why? Well because the
`map_apply` function used for the `Reflect::apply` impls on `Map` types
depends on `Map::insert_boxed`, which (at least for `DynamicMap`)
requires `Reflect::reflect_hash`. The same would apply when adding
support for reflection-based diffing, which will require
`Reflect::reflect_partial_eq`.
Again, this is a totally viable alternative. I just chose not to go with
it for the reasons above. If we want to go with it, then we can close
this PR and we can pursue this alternative instead.
#### Downsides
Just to highlight a quick potential downside (likely needs more
investigation): retrieving the `TypeInfo` requires acquiring a lock on
the `GenericTypeInfoCell` used by the `Typed` impls for generic types
(non-generic types use a `OnceBox which should be faster). I am not sure
how much of a performance hit that is and will need to run some
benchmarks to compare against.
</details>
### Open Questions
1. Should we use `Cow<'static, TypeInfo>` instead? I think that might be
easier for modding? Perhaps, in that case, we need to update
`Typed::type_info` and friends as well?
2. Are the alternatives better than the approach this PR takes? Are
there other alternatives?
---
## Changelog
### Changed
- `Reflect::get_type_info` has been renamed to
`Reflect::represented_type_info`
- This method now returns `Option<&'static TypeInfo>` rather than just
`&'static TypeInfo`
### Added
- Added `Reflect::is_dynamic` method to indicate when a type is dynamic
- Added a `set_represented_type` method on all dynamic types
### Removed
- Removed `TypeInfo::Dynamic` (use `Reflect::is_dynamic` instead)
- Removed `Typed` impls for all dynamic types
## Migration Guide
- The Dynamic types no longer take a string type name. Instead, they
require a static reference to `TypeInfo`:
```rust
#[derive(Reflect)]
struct MyTupleStruct(f32, f32);
let mut dyn_tuple_struct = DynamicTupleStruct::default();
dyn_tuple_struct.insert(1.23_f32);
dyn_tuple_struct.insert(3.21_f32);
// BEFORE:
let type_name = std::any::type_name::<MyTupleStruct>();
dyn_tuple_struct.set_name(type_name);
// AFTER:
let type_info = <MyTupleStruct as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
```
- `Reflect::get_type_info` has been renamed to
`Reflect::represented_type_info` and now also returns an
`Option<&'static TypeInfo>` (instead of just `&'static TypeInfo`):
```rust
// BEFORE:
let info: &'static TypeInfo = value.get_type_info();
// AFTER:
let info: &'static TypeInfo = value.represented_type_info().unwrap();
```
- `TypeInfo::Dynamic` and `DynamicInfo` has been removed. Use
`Reflect::is_dynamic` instead:
```rust
// BEFORE:
if matches!(value.get_type_info(), TypeInfo::Dynamic) {
// ...
}
// AFTER:
if value.is_dynamic() {
// ...
}
```
---------
Co-authored-by: radiish <cb.setho@gmail.com>
2023-04-26 12:17:46 +00:00
|
|
|
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
|
|
|
Some(<Self as Typed>::type_info())
|
2022-12-03 03:35:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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> {
|
2023-02-17 15:37:35 +00:00
|
|
|
let mut hasher = reflect_hasher();
|
2022-12-03 03:35:45 +00:00
|
|
|
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());
|
2022-07-19 23:00:34 +00:00
|
|
|
registration.insert::<ReflectFromPtr>(FromType::<Cow<'static, str>>::from_type());
|
2022-06-20 17:18:58 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
|
|
|
|
impl FromReflect for Cow<'static, str> {
|
|
|
|
fn from_reflect(reflect: &dyn crate::Reflect) -> Option<Self> {
|
Make `Reflect` safe to implement (#5010)
# Objective
Currently, `Reflect` is unsafe to implement because of a contract in which `any` and `any_mut` must return `self`, or `downcast` will cause UB. This PR makes `Reflect` safe, makes `downcast` not use unsafe, and eliminates this contract.
## Solution
This PR adds a method to `Reflect`, `any`. It also renames the old `any` to `as_any`.
`any` now takes a `Box<Self>` and returns a `Box<dyn Any>`.
---
## Changelog
### Added:
- `any()` method
- `represents()` method
### Changed:
- `Reflect` is now a safe trait
- `downcast()` is now safe
- The old `any` is now called `as_any`, and `any_mut` is now `as_mut_any`
## Migration Guide
- Reflect derives should not have to change anything
- Manual reflect impls will need to remove the `unsafe` keyword, add `any()` implementations, and rename the old `any` and `any_mut` to `as_any` and `as_mut_any`.
- Calls to `any`/`any_mut` must be changed to `as_any`/`as_mut_any`
## Points of discussion:
- Should renaming `any` be avoided and instead name the new method `any_box`?
- ~~Could there be a performance regression from avoiding the unsafe? I doubt it, but this change does seem to introduce redundant checks.~~
- ~~Could/should `is` and `type_id()` be implemented differently? For example, moving `is` onto `Reflect` as an `fn(&self, TypeId) -> bool`~~
Co-authored-by: PROMETHIA-27 <42193387+PROMETHIA-27@users.noreply.github.com>
2022-06-27 16:52:25 +00:00
|
|
|
Some(
|
|
|
|
reflect
|
|
|
|
.as_any()
|
|
|
|
.downcast_ref::<Cow<'static, str>>()?
|
|
|
|
.clone(),
|
|
|
|
)
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-29 21:04:26 +00:00
|
|
|
|
2022-12-03 03:35:45 +00:00
|
|
|
impl Reflect for &'static Path {
|
|
|
|
fn type_name(&self) -> &str {
|
|
|
|
std::any::type_name::<Self>()
|
|
|
|
}
|
|
|
|
|
bevy_reflect: Better proxies (#6971)
# Objective
> This PR is based on discussion from #6601
The Dynamic types (e.g. `DynamicStruct`, `DynamicList`, etc.) act as
both:
1. Dynamic containers which may hold any arbitrary data
2. Proxy types which may represent any other type
Currently, the only way we can represent the proxy-ness of a Dynamic is
by giving it a name.
```rust
// This is just a dynamic container
let mut data = DynamicStruct::default();
// This is a "proxy"
data.set_name(std::any::type_name::<Foo>());
```
This type name is the only way we check that the given Dynamic is a
proxy of some other type. When we need to "assert the type" of a `dyn
Reflect`, we call `Reflect::type_name` on it. However, because we're
only using a string to denote the type, we run into a few gotchas and
limitations.
For example, hashing a Dynamic proxy may work differently than the type
it proxies:
```rust
#[derive(Reflect, Hash)]
#[reflect(Hash)]
struct Foo(i32);
let concrete = Foo(123);
let dynamic = concrete.clone_dynamic();
let concrete_hash = concrete.reflect_hash();
let dynamic_hash = dynamic.reflect_hash();
// The hashes are not equal because `concrete` uses its own `Hash` impl
// while `dynamic` uses a reflection-based hashing algorithm
assert_ne!(concrete_hash, dynamic_hash);
```
Because the Dynamic proxy only knows about the name of the type, it's
unaware of any other information about it. This means it also differs on
`Reflect::reflect_partial_eq`, and may include ignored or skipped fields
in places the concrete type wouldn't.
## Solution
Rather than having Dynamics pass along just the type name of proxied
types, we can instead have them pass around the `TypeInfo`.
Now all Dynamic types contain an `Option<&'static TypeInfo>` rather than
a `String`:
```diff
pub struct DynamicTupleStruct {
- type_name: String,
+ represented_type: Option<&'static TypeInfo>,
fields: Vec<Box<dyn Reflect>>,
}
```
By changing `Reflect::get_type_info` to
`Reflect::represented_type_info`, hopefully we make this behavior a
little clearer. And to account for `None` values on these dynamic types,
`Reflect::represented_type_info` now returns `Option<&'static
TypeInfo>`.
```rust
let mut data = DynamicTupleStruct::default();
// Not proxying any specific type
assert!(dyn_tuple_struct.represented_type_info().is_none());
let type_info = <Foo as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
// Alternatively:
// let dyn_tuple_struct = foo.clone_dynamic();
// Now we're proxying `Foo`
assert!(dyn_tuple_struct.represented_type_info().is_some());
```
This means that we can have full access to all the static type
information for the proxied type. Future work would include
transitioning more static type information (trait impls, attributes,
etc.) over to the `TypeInfo` so it can actually be utilized by Dynamic
proxies.
### Alternatives & Rationale
> **Note**
> These alternatives were written when this PR was first made using a
`Proxy` trait. This trait has since been removed.
<details>
<summary>View</summary>
#### Alternative: The `Proxy<T>` Approach
I had considered adding something like a `Proxy<T>` type where `T` would
be the Dynamic and would contain the proxied type information.
This was nice in that it allows us to explicitly determine whether
something is a proxy or not at a type level. `Proxy<DynamicStruct>`
proxies a struct. Makes sense.
The reason I didn't go with this approach is because (1) tuples, (2)
complexity, and (3) `PartialReflect`.
The `DynamicTuple` struct allows us to represent tuples at runtime. It
also allows us to do something you normally can't with tuples: add new
fields. Because of this, adding a field immediately invalidates the
proxy (e.g. our info for `(i32, i32)` doesn't apply to `(i32, i32,
NewField)`). By going with this PR's approach, we can just remove the
type info on `DynamicTuple` when that happens. However, with the
`Proxy<T>` approach, it becomes difficult to represent this behavior—
we'd have to completely control how we access data for `T` for each `T`.
Secondly, it introduces some added complexities (aside from the manual
impls for each `T`). Does `Proxy<T>` impl `Reflect`? Likely yes, if we
want to represent it as `dyn Reflect`. What `TypeInfo` do we give it?
How would we forward reflection methods to the inner type (remember, we
don't have specialization)? How do we separate this from Dynamic types?
And finally, how do all this in a way that's both logical and intuitive
for users?
Lastly, introducing a `Proxy` trait rather than a `Proxy<T>` struct is
actually more inline with the [Unique Reflect
RFC](https://github.com/bevyengine/rfcs/pull/56). In a way, the `Proxy`
trait is really one part of the `PartialReflect` trait introduced in
that RFC (it's technically not in that RFC but it fits well with it),
where the `PartialReflect` serves as a way for proxies to work _like_
concrete types without having full access to everything a concrete
`Reflect` type can do. This would help bridge the gap between the
current state of the crate and the implementation of that RFC.
All that said, this is still a viable solution. If the community
believes this is the better path forward, then we can do that instead.
These were just my reasons for not initially going with it in this PR.
#### Alternative: The Type Registry Approach
The `Proxy` trait is great and all, but how does it solve the original
problem? Well, it doesn't— yet!
The goal would be to start moving information from the derive macro and
its attributes to the generated `TypeInfo` since these are known
statically and shouldn't change. For example, adding `ignored: bool` to
`[Un]NamedField` or a list of impls.
However, there is another way of storing this information. This is, of
course, one of the uses of the `TypeRegistry`. If we're worried about
Dynamic proxies not aligning with their concrete counterparts, we could
move more type information to the registry and require its usage.
For example, we could replace `Reflect::reflect_hash(&self)` with
`Reflect::reflect_hash(&self, registry: &TypeRegistry)`.
That's not the _worst_ thing in the world, but it is an ergonomics loss.
Additionally, other attributes may have their own requirements, further
restricting what's possible without the registry. The `Reflect::apply`
method will require the registry as well now. Why? Well because the
`map_apply` function used for the `Reflect::apply` impls on `Map` types
depends on `Map::insert_boxed`, which (at least for `DynamicMap`)
requires `Reflect::reflect_hash`. The same would apply when adding
support for reflection-based diffing, which will require
`Reflect::reflect_partial_eq`.
Again, this is a totally viable alternative. I just chose not to go with
it for the reasons above. If we want to go with it, then we can close
this PR and we can pursue this alternative instead.
#### Downsides
Just to highlight a quick potential downside (likely needs more
investigation): retrieving the `TypeInfo` requires acquiring a lock on
the `GenericTypeInfoCell` used by the `Typed` impls for generic types
(non-generic types use a `OnceBox which should be faster). I am not sure
how much of a performance hit that is and will need to run some
benchmarks to compare against.
</details>
### Open Questions
1. Should we use `Cow<'static, TypeInfo>` instead? I think that might be
easier for modding? Perhaps, in that case, we need to update
`Typed::type_info` and friends as well?
2. Are the alternatives better than the approach this PR takes? Are
there other alternatives?
---
## Changelog
### Changed
- `Reflect::get_type_info` has been renamed to
`Reflect::represented_type_info`
- This method now returns `Option<&'static TypeInfo>` rather than just
`&'static TypeInfo`
### Added
- Added `Reflect::is_dynamic` method to indicate when a type is dynamic
- Added a `set_represented_type` method on all dynamic types
### Removed
- Removed `TypeInfo::Dynamic` (use `Reflect::is_dynamic` instead)
- Removed `Typed` impls for all dynamic types
## Migration Guide
- The Dynamic types no longer take a string type name. Instead, they
require a static reference to `TypeInfo`:
```rust
#[derive(Reflect)]
struct MyTupleStruct(f32, f32);
let mut dyn_tuple_struct = DynamicTupleStruct::default();
dyn_tuple_struct.insert(1.23_f32);
dyn_tuple_struct.insert(3.21_f32);
// BEFORE:
let type_name = std::any::type_name::<MyTupleStruct>();
dyn_tuple_struct.set_name(type_name);
// AFTER:
let type_info = <MyTupleStruct as Typed>::type_info();
dyn_tuple_struct.set_represented_type(Some(type_info));
```
- `Reflect::get_type_info` has been renamed to
`Reflect::represented_type_info` and now also returns an
`Option<&'static TypeInfo>` (instead of just `&'static TypeInfo`):
```rust
// BEFORE:
let info: &'static TypeInfo = value.get_type_info();
// AFTER:
let info: &'static TypeInfo = value.represented_type_info().unwrap();
```
- `TypeInfo::Dynamic` and `DynamicInfo` has been removed. Use
`Reflect::is_dynamic` instead:
```rust
// BEFORE:
if matches!(value.get_type_info(), TypeInfo::Dynamic) {
// ...
}
// AFTER:
if value.is_dynamic() {
// ...
}
```
---------
Co-authored-by: radiish <cb.setho@gmail.com>
2023-04-26 12:17:46 +00:00
|
|
|
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
|
|
|
Some(<Self as Typed>::type_info())
|
2022-12-03 03:35:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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> {
|
2023-02-17 15:37:35 +00:00
|
|
|
let mut hasher = reflect_hasher();
|
2022-12-03 03:35:45 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-25 23:49:26 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-08 19:19:19 +00:00
|
|
|
impl Reflect for Cow<'static, Path> {
|
|
|
|
fn type_name(&self) -> &str {
|
|
|
|
std::any::type_name::<Self>()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
|
|
|
|
Some(<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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
fmt::Debug::fmt(&self, f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Typed for Cow<'static, Path> {
|
|
|
|
fn type_info() -> &'static TypeInfo {
|
|
|
|
static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
|
|
|
|
CELL.get_or_set(|| TypeInfo::Value(ValueInfo::new::<Self>()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromReflect for Cow<'static, Path> {
|
|
|
|
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
|
|
|
|
Some(reflect.as_any().downcast_ref::<Self>()?.clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GetTypeRegistration for Cow<'static, Path> {
|
|
|
|
fn get_type_registration() -> TypeRegistration {
|
|
|
|
let mut registration = TypeRegistration::of::<Self>();
|
|
|
|
registration.insert::<ReflectDeserialize>(FromType::<Self>::from_type());
|
|
|
|
registration.insert::<ReflectFromPtr>(FromType::<Self>::from_type());
|
|
|
|
registration.insert::<ReflectSerialize>(FromType::<Self>::from_type());
|
|
|
|
registration.insert::<ReflectFromReflect>(FromType::<Self>::from_type());
|
|
|
|
registration
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-29 21:04:26 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2022-08-17 00:21:15 +00:00
|
|
|
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::{
|
2022-08-17 00:21:15 +00:00
|
|
|
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
|
|
|
};
|
Derived `Copy` trait for `bevy_input` events, `Serialize`/`Deserialize` for events in `bevy_input` and `bevy_windows`, `PartialEq` for events in both, and `Eq` where possible in both. (#6023)
# Objective
Add traits to events in `bevy_input` and `bevy_windows`: `Copy`, `Serialize`/`Deserialize`, `PartialEq`, and `Eq`, as requested in https://github.com/bevyengine/bevy/issues/6022, https://github.com/bevyengine/bevy/issues/6023, https://github.com/bevyengine/bevy/issues/6024.
## Solution
Added the traits to events in `bevy_input` and `bevy_windows`. Added dependency of `serde` in `Cargo.toml` of `bevy_input`.
## Migration Guide
If one has been `.clone()`'ing `bevy_input` events, Clippy will now complain about that. Just remove `.clone()` to solve.
## Other Notes
Some events in `bevy_input` had `f32` fields, so `Eq` trait was not derived for them.
Some events in `bevy_windows` had `String` fields, so `Copy` trait was not derived for them.
Co-authored-by: targrub <62773321+targrub@users.noreply.github.com>
2022-09-20 18:24:00 +00:00
|
|
|
use bevy_utils::HashMap;
|
|
|
|
use bevy_utils::{Duration, Instant};
|
2022-04-26 19:41:26 +00:00
|
|
|
use std::f32::consts::{PI, TAU};
|
2022-11-25 23:49:26 +00:00
|
|
|
use std::path::Path;
|
2021-12-29 21:04:26 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn can_serialize_duration() {
|
2022-06-20 17:18:58 +00:00
|
|
|
let mut type_registry = TypeRegistry::default();
|
Derived `Copy` trait for `bevy_input` events, `Serialize`/`Deserialize` for events in `bevy_input` and `bevy_windows`, `PartialEq` for events in both, and `Eq` where possible in both. (#6023)
# Objective
Add traits to events in `bevy_input` and `bevy_windows`: `Copy`, `Serialize`/`Deserialize`, `PartialEq`, and `Eq`, as requested in https://github.com/bevyengine/bevy/issues/6022, https://github.com/bevyengine/bevy/issues/6023, https://github.com/bevyengine/bevy/issues/6024.
## Solution
Added the traits to events in `bevy_input` and `bevy_windows`. Added dependency of `serde` in `Cargo.toml` of `bevy_input`.
## Migration Guide
If one has been `.clone()`'ing `bevy_input` events, Clippy will now complain about that. Just remove `.clone()` to solve.
## Other Notes
Some events in `bevy_input` had `f32` fields, so `Eq` trait was not derived for them.
Some events in `bevy_windows` had `String` fields, so `Copy` trait was not derived for them.
Co-authored-by: targrub <62773321+targrub@users.noreply.github.com>
2022-09-20 18:24:00 +00:00
|
|
|
type_registry.register::<Duration>();
|
2022-06-20 17:18:58 +00:00
|
|
|
|
|
|
|
let reflect_serialize = type_registry
|
Derived `Copy` trait for `bevy_input` events, `Serialize`/`Deserialize` for events in `bevy_input` and `bevy_windows`, `PartialEq` for events in both, and `Eq` where possible in both. (#6023)
# Objective
Add traits to events in `bevy_input` and `bevy_windows`: `Copy`, `Serialize`/`Deserialize`, `PartialEq`, and `Eq`, as requested in https://github.com/bevyengine/bevy/issues/6022, https://github.com/bevyengine/bevy/issues/6023, https://github.com/bevyengine/bevy/issues/6024.
## Solution
Added the traits to events in `bevy_input` and `bevy_windows`. Added dependency of `serde` in `Cargo.toml` of `bevy_input`.
## Migration Guide
If one has been `.clone()`'ing `bevy_input` events, Clippy will now complain about that. Just remove `.clone()` to solve.
## Other Notes
Some events in `bevy_input` had `f32` fields, so `Eq` trait was not derived for them.
Some events in `bevy_windows` had `String` fields, so `Copy` trait was not derived for them.
Co-authored-by: targrub <62773321+targrub@users.noreply.github.com>
2022-09-20 18:24:00 +00:00
|
|
|
.get_type_data::<ReflectSerialize>(std::any::TypeId::of::<Duration>())
|
2022-06-20 17:18:58 +00:00
|
|
|
.unwrap();
|
Derived `Copy` trait for `bevy_input` events, `Serialize`/`Deserialize` for events in `bevy_input` and `bevy_windows`, `PartialEq` for events in both, and `Eq` where possible in both. (#6023)
# Objective
Add traits to events in `bevy_input` and `bevy_windows`: `Copy`, `Serialize`/`Deserialize`, `PartialEq`, and `Eq`, as requested in https://github.com/bevyengine/bevy/issues/6022, https://github.com/bevyengine/bevy/issues/6023, https://github.com/bevyengine/bevy/issues/6024.
## Solution
Added the traits to events in `bevy_input` and `bevy_windows`. Added dependency of `serde` in `Cargo.toml` of `bevy_input`.
## Migration Guide
If one has been `.clone()`'ing `bevy_input` events, Clippy will now complain about that. Just remove `.clone()` to solve.
## Other Notes
Some events in `bevy_input` had `f32` fields, so `Eq` trait was not derived for them.
Some events in `bevy_windows` had `String` fields, so `Copy` trait was not derived for them.
Co-authored-by: targrub <62773321+targrub@users.noreply.github.com>
2022-09-20 18:24:00 +00:00
|
|
|
let _serializable = reflect_serialize.get_serializable(&Duration::ZERO);
|
2021-12-29 21:04:26 +00:00
|
|
|
}
|
2022-04-26 19:41:26 +00:00
|
|
|
|
2022-05-17 23:45:09 +00:00
|
|
|
#[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());
|
|
|
|
}
|
|
|
|
|
2022-04-26 19:41:26 +00:00
|
|
|
#[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() {
|
2022-04-26 19:41:26 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-08-17 00:21:15 +00:00
|
|
|
#[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);
|
|
|
|
}
|
|
|
|
|
2022-08-24 20:44:35 +00:00
|
|
|
#[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`");
|
|
|
|
}
|
2022-04-26 19:41:26 +00:00
|
|
|
}
|
2022-08-04 22:09:49 +00:00
|
|
|
#[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());
|
|
|
|
}
|
2022-08-17 00:21:15 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn instant_should_from_reflect() {
|
|
|
|
let expected = Instant::now();
|
|
|
|
let output = <Instant as FromReflect>::from_reflect(&expected).unwrap();
|
|
|
|
assert_eq!(expected, output);
|
|
|
|
}
|
2022-11-25 23:49:26 +00:00
|
|
|
|
|
|
|
#[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);
|
|
|
|
}
|
2021-12-29 21:04:26 +00:00
|
|
|
}
|