Skip to content

Commit

Permalink
remove blanket Serialize + Deserialize requirement for Reflect on…
Browse files Browse the repository at this point in the history
… generic types (bevyengine#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 (bevyengine#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.
  • Loading branch information
jakobhellermann authored and inodentry committed Aug 8, 2022
1 parent 59f9805 commit 53dbc2a
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 33 deletions.
42 changes: 41 additions & 1 deletion crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,14 @@ impl App {
self
}

/// Adds the type `T` to the type registry [`Resource`].
/// Registers the type `T` in the [`TypeRegistry`](bevy_reflect::TypeRegistry) resource,
/// adding reflect data as specified in the [`Reflect`](bevy_reflect::Reflect) derive:
/// ```rust,ignore
/// #[derive(Reflect)]
/// #[reflect(Component, Serialize, Deserialize)] // will register ReflectComponent, ReflectSerialize, ReflectDeserialize
/// ```
///
/// See [`bevy_reflect::TypeRegistry::register`].
#[cfg(feature = "bevy_reflect")]
pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
{
Expand All @@ -872,6 +879,39 @@ impl App {
self
}

/// Adds the type data `D` to type `T` in the [`TypeRegistry`](bevy_reflect::TypeRegistry) resource.
///
/// Most of the time [`App::register_type`] can be used instead to register a type you derived [`Reflect`](bevy_reflect::Reflect) for.
/// However, in cases where you want to add a piece of type data that was not included in the list of `#[reflect(...)]` type data in the derive,
/// or where the type is generic and cannot register e.g. `ReflectSerialize` unconditionally without knowing the specific type parameters,
/// this method can be used to insert additional type data.
///
/// # Example
/// ```rust
/// use bevy_app::App;
/// use bevy_reflect::{ReflectSerialize, ReflectDeserialize};
///
/// App::new()
/// .register_type::<Option<String>>()
/// .register_type_data::<Option<String>, ReflectSerialize>()
/// .register_type_data::<Option<String>, ReflectDeserialize>();
/// ```
///
/// See [`bevy_reflect::TypeRegistry::register_type_data`].
#[cfg(feature = "bevy_reflect")]
pub fn register_type_data<
T: bevy_reflect::Reflect + 'static,
D: bevy_reflect::TypeData + bevy_reflect::FromType<T>,
>(
&mut self,
) -> &mut Self {
{
let registry = self.world.resource_mut::<bevy_reflect::TypeRegistryArc>();
registry.write().register_type_data::<T, D>();
}
self
}

/// Adds an [`App`] as a child of the current one.
///
/// The provided function `f` is called by the [`update`](Self::update) method. The [`World`]
Expand Down
37 changes: 14 additions & 23 deletions crates/bevy_reflect/src/impls/std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ use crate::{

use crate::utility::{GenericTypeInfoCell, NonGenericTypeInfoCell};
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value};
use bevy_utils::{Duration, HashMap, HashSet};
use serde::{Deserialize, Serialize};
use bevy_utils::{Duration, HashMap, HashSet, Instant};
use std::{
any::Any,
borrow::Cow,
Expand All @@ -33,10 +32,12 @@ impl_reflect_value!(isize(Debug, Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(f32(Debug, PartialEq, Serialize, Deserialize));
impl_reflect_value!(f64(Debug, PartialEq, Serialize, Deserialize));
impl_reflect_value!(String(Debug, Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(Option<T: Serialize + Clone + for<'de> Deserialize<'de> + Reflect + 'static>(Serialize, Deserialize));
impl_reflect_value!(HashSet<T: Serialize + Hash + Eq + Clone + for<'de> Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize));
impl_reflect_value!(Range<T: Serialize + Clone + for<'de> Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize));
impl_reflect_value!(Option<T: Clone + Reflect + 'static>());
impl_reflect_value!(Result<T: Clone + Reflect + 'static, E: Clone + Reflect + 'static>());
impl_reflect_value!(HashSet<T: Hash + Eq + Clone + Send + Sync + 'static>());
impl_reflect_value!(Range<T: Clone + Send + Sync + 'static>());
impl_reflect_value!(Duration(Debug, Hash, PartialEq, Serialize, Deserialize));
impl_reflect_value!(Instant(Debug, Hash, PartialEq));

impl_from_reflect_value!(bool);
impl_from_reflect_value!(char);
Expand All @@ -55,15 +56,9 @@ impl_from_reflect_value!(isize);
impl_from_reflect_value!(f32);
impl_from_reflect_value!(f64);
impl_from_reflect_value!(String);
impl_from_reflect_value!(
Option<T: Serialize + Clone + for<'de> Deserialize<'de> + Reflect + 'static>
);
impl_from_reflect_value!(
HashSet<T: Serialize + Hash + Eq + Clone + for<'de> Deserialize<'de> + Send + Sync + 'static>
);
impl_from_reflect_value!(
Range<T: Serialize + Clone + for<'de> Deserialize<'de> + Send + Sync + 'static>
);
impl_from_reflect_value!(Option<T: Clone + Reflect + 'static>);
impl_from_reflect_value!(HashSet<T: Hash + Eq + Clone + Send + Sync + 'static>);
impl_from_reflect_value!(Range<T: Clone + Send + Sync + 'static>);
impl_from_reflect_value!(Duration);

impl<T: FromReflect> Array for Vec<T> {
Expand Down Expand Up @@ -171,10 +166,9 @@ impl<T: FromReflect> Typed for Vec<T> {
}
}

impl<T: FromReflect + for<'de> Deserialize<'de>> GetTypeRegistration for Vec<T> {
impl<T: FromReflect> GetTypeRegistration for Vec<T> {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Vec<T>>();
registration.insert::<ReflectDeserialize>(FromType::<Vec<T>>::from_type());
registration.insert::<ReflectFromPtr>(FromType::<Vec<T>>::from_type());
registration
}
Expand Down Expand Up @@ -323,12 +317,11 @@ impl<K: FromReflect + Eq + Hash, V: FromReflect> Typed for HashMap<K, V> {

impl<K, V> GetTypeRegistration for HashMap<K, V>
where
K: FromReflect + Clone + Eq + Hash + for<'de> Deserialize<'de>,
V: FromReflect + Clone + for<'de> Deserialize<'de>,
K: FromReflect + Clone + Eq + Hash,
V: FromReflect + Clone,
{
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<HashMap<K, V>>();
registration.insert::<ReflectDeserialize>(FromType::<HashMap<K, V>>::from_type());
registration.insert::<ReflectFromPtr>(FromType::<HashMap<K, V>>::from_type());
registration
}
Expand Down Expand Up @@ -476,11 +469,9 @@ impl<T: Reflect, const N: usize> Typed for [T; N] {
macro_rules! impl_array_get_type_registration {
($($N:expr)+) => {
$(
impl<T: Reflect + for<'de> Deserialize<'de>> GetTypeRegistration for [T; $N] {
impl<T: Reflect > GetTypeRegistration for [T; $N] {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<[T; $N]>();
registration.insert::<ReflectDeserialize>(FromType::<[T; $N]>::from_type());
registration
TypeRegistration::of::<[T; $N]>()
}
}
)+
Expand Down
11 changes: 4 additions & 7 deletions crates/bevy_reflect/src/tuple.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use crate::utility::NonGenericTypeInfoCell;
use crate::{
DynamicInfo, FromReflect, FromType, GetTypeRegistration, Reflect, ReflectDeserialize,
ReflectMut, ReflectRef, TypeInfo, TypeRegistration, Typed, UnnamedField,
DynamicInfo, FromReflect, GetTypeRegistration, Reflect, ReflectMut, ReflectRef, TypeInfo,
TypeRegistration, Typed, UnnamedField,
};
use serde::Deserialize;
use std::any::{Any, TypeId};
use std::fmt::{Debug, Formatter};
use std::slice::Iter;
Expand Down Expand Up @@ -534,11 +533,9 @@ macro_rules! impl_reflect_tuple {
}
}

impl<$($name: Reflect + Typed + for<'de> Deserialize<'de>),*> GetTypeRegistration for ($($name,)*) {
impl<$($name: Reflect + Typed),*> GetTypeRegistration for ($($name,)*) {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<($($name,)*)>();
registration.insert::<ReflectDeserialize>(FromType::<($($name,)*)>::from_type());
registration
TypeRegistration::of::<($($name,)*)>()
}
}

Expand Down
33 changes: 32 additions & 1 deletion crates/bevy_reflect/src/type_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ impl TypeRegistry {
registry
}

/// Registers the type `T`.
/// Registers the type `T`, adding reflect data as specified in the [`Reflect`] derive:
/// ```rust,ignore
/// #[derive(Reflect)]
/// #[reflect(Component, Serialize, Deserialize)] // will register ReflectComponent, ReflectSerialize, ReflectDeserialize
/// ```
pub fn register<T>(&mut self)
where
T: GetTypeRegistration,
Expand All @@ -100,6 +104,33 @@ impl TypeRegistry {
.insert(registration.type_id(), registration);
}

/// Registers the type data `D` for type `T`.
///
/// Most of the time [`TypeRegistry::register`] can be used instead to register a type you derived [`Reflect`] for.
/// However, in cases where you want to add a piece of type data that was not included in the list of `#[reflect(...)]` type data in the derive,
/// or where the type is generic and cannot register e.g. `ReflectSerialize` unconditionally without knowing the specific type parameters,
/// this method can be used to insert additional type data.
///
/// # Example
/// ```rust
/// use bevy_reflect::{TypeRegistry, ReflectSerialize, ReflectDeserialize};
///
/// let mut type_registry = TypeRegistry::default();
/// type_registry.register::<Option<String>>();
/// type_registry.register_type_data::<Option<String>, ReflectSerialize>();
/// type_registry.register_type_data::<Option<String>, ReflectDeserialize>();
/// ```
pub fn register_type_data<T: Reflect + 'static, D: TypeData + FromType<T>>(&mut self) {
let data = self.get_mut(TypeId::of::<T>()).unwrap_or_else(|| {
panic!(
"attempted to call `TypeRegistry::register_type_data` for type `{T}` with data `{D}` without registering `{T}` first",
T = std::any::type_name::<T>(),
D = std::any::type_name::<D>(),
)
});
data.insert(D::from_type());
}

/// Returns a reference to the [`TypeRegistration`] of the type with the
/// given [`TypeId`].
///
Expand Down
5 changes: 4 additions & 1 deletion crates/bevy_time/src/time.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use bevy_ecs::reflect::ReflectResource;
use bevy_reflect::Reflect;
use bevy_utils::{Duration, Instant};

/// Tracks elapsed time since the last update and since the App has started
#[derive(Debug, Clone)]
#[derive(Reflect, Debug, Clone)]
#[reflect(Resource)]
pub struct Time {
delta: Duration,
last_update: Option<Instant>,
Expand Down

0 comments on commit 53dbc2a

Please sign in to comment.