From c9fabbdded9450585a8f1ae6c42f72bac8638646 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 9 Jan 2023 23:49:54 -0700 Subject: [PATCH 01/15] Updated crate-level docs --- crates/bevy_reflect/src/lib.rs | 371 ++++++++++++++++++++++++++++++++- 1 file changed, 370 insertions(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index c56debd98302d..aec509beb07bf 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -1,4 +1,373 @@ -#![doc = include_str!("../README.md")] +//! Reflection in Rust. +//! +//! [Reflection] is a powerful tool provided within many programming languages +//! that allows for meta-programming: using information _about_ the program to +//! _affect_ the program. +//! In other words, reflection allows us to inspect the program itself, its +//! syntax, and its type information at runtime. +//! +//! While Rust doesn't yet provide much built-in tooling for reflection, +//! this crate serves to help fill that role. +//! And while it was made with the [Bevy] game engine in mind, +//! it's a general-purpose solution that can be used in any Rust project. +//! +//! At a very high level, this crate allows you to: +//! * Dynamically interact with Rust values +//! * Access type metadata at runtime +//! * Serialize and deserialize (i.e. save and load) data +//! +//! # The `Reflect` Trait +//! +//! At the core of [`bevy_reflect`] is the [`Reflect`] trait. +//! +//! One of its primary purposes is to allow all implementors to be passed around +//! as a `dyn Reflect` trait object. +//! This allows any such type to be operated upon completely dynamically (at a small [runtime cost]). +//! +//! Implementing the trait is easily done using the provided [derive macro]: +//! +//! ``` +//! # use bevy_reflect::Reflect; +//! #[derive(Reflect)] +//! struct MyStruct { +//! foo: i32 +//! } +//! ``` +//! +//! This will automatically generate the implementation of `Reflect` for any struct or enum. +//! +//! It will also generate other very important trait implementations used for reflection: +//! * [`GetTypeRegistration`] +//! * [`Typed`] +//! * [`Struct`], [`TupleStruct`], or [`Enum`] depending on the type +//! +//! ## Requirements +//! +//! We can implement `Reflect` on any type that satisfies _both_ of the following conditions: +//! * The type implements `Any`. +//! In other words, the type itself has a `'static` lifetime. +//! * All fields and sub-elements themselves implement `Reflect` +//! (see the [derive macro documentation] for details on how to ignore certain fields when deriving). +//! +//! Additionally, using the derive macro on enums requires a third condition to be met: +//! * All fields and sub-elements must implement [`FromReflect`]— +//! another important reflection trait discussed in a later section. +//! +//! # The `Reflect` Subtraits +//! +//! Since [`Reflect`] is meant to cover any and every type, this crate also comes with a few +//! more traits to accompany `Reflect` and provide more specific interactions. +//! We refer to these traits as the _reflection subtraits_ since they all have `Reflect` as a supertrait. +//! The current list of reflection subtraits include: +//! * [`Tuple`] +//! * [`Array`] +//! * [`List`] +//! * [`Map`] +//! * [`Struct`] +//! * [`TupleStruct`] +//! * [`Enum`] +//! +//! As mentioned previously, the last three are automatically implemented by the [derive macro]. +//! +//! Each of these traits come with their own methods specific to their respective category. +//! For example, we can access our struct's fields by name using the [`Struct::field`] method. +//! +//! ``` +//! # use bevy_reflect::{Reflect, Struct}; +//! # #[derive(Reflect)] +//! # struct MyStruct { +//! # foo: i32 +//! # } +//! let my_struct: Box = Box::new(MyStruct { +//! foo: 123 +//! }); +//! let foo: &dyn Reflect = my_struct.field("foo").unwrap(); +//! assert_eq!(Some(&123), foo.downcast_ref::()); +//! ``` +//! +//! Since most data is passed around as `dyn Reflect`, +//! the `Reflect` trait has methods for going to and from these subtraits. +//! +//! [`Reflect::reflect_ref`], [`Reflect::reflect_mut`], and [`Reflect::reflect_owned`] all return +//! an enum that respectively contains immutable, mutable, and owned access to the type as a subtrait object. +//! +//! For example, we can get out a `dyn Tuple` from our reflected tuple type using one of these methods. +//! +//! ``` +//! # use bevy_reflect::{Reflect, ReflectRef}; +//! let my_tuple: Box = Box::new((1, 2, 3)); +//! let ReflectRef::Tuple(my_tuple) = my_tuple.reflect_ref() else { unreachable!() }; +//! assert_eq!(3, my_tuple.field_len()); +//! ``` +//! +//! And to go back to a general-purpose `dyn Reflect`, +//! we can just use of the matching [`Reflect::as_reflect`], [`Reflect::as_reflect_mut`], +//! or [`Reflect::into_reflect`] methods. +//! +//! ## Value Types +//! +//! Types that do not fall under one of the above subtraits, +//! such as for primitives (e.g. `bool`, `usize`, etc.) +//! and simple types (e.g. `String`, `Duration`), +//! are referred to as _value_ types +//! since methods like [`Reflect::reflect_ref`] return a [`ReflectRef::Value`] variant. +//! While most other types contain their own `dyn Reflect` fields and data, +//! these types generally cannot be broken down any further. +//! +//! # Dynamic Types +//! +//! Each subtrait comes with a corresponding _dynamic_ type. +//! +//! The available dynamic types are: +//! * [`DynamicTuple`] +//! * [`DynamicArray`] +//! * [`DynamicList`] +//! * [`DynamicMap`] +//! * [`DynamicStruct`] +//! * [`DynamicTupleStruct`] +//! * [`DynamicEnum`] +//! +//! These dynamic types may contain any arbitrary reflected data. +//! +//! ``` +//! # use bevy_reflect::{DynamicStruct, Struct}; +//! let mut data = DynamicStruct::default(); +//! data.insert("foo", 123_i32); +//! assert_eq!(Some(&123), data.field("foo").unwrap().downcast_ref::()) +//! ``` +//! +//! They are most commonly used as "proxies" for other types, +//! where they contain the same data as— and therefore, represent— a concrete type. +//! The [`Reflect::clone_value`] will return a dynamic type for all non-value types, +//! allowing all types to essentially be "cloned". +//! And since dynamic types themselves implement [`Reflect`], +//! we may pass them around just like any other reflected type. +//! +//! ``` +//! # use bevy_reflect::{DynamicStruct, Reflect}; +//! # #[derive(Reflect)] +//! # struct MyStruct { +//! # foo: i32 +//! # } +//! let original: Box = Box::new(MyStruct { +//! foo: 123 +//! }); +//! +//! // `cloned` will be a `DynamicStruct` representing a `MyStruct` +//! let cloned: Box = original.clone_value(); +//! assert!(cloned.represents::()); +//! assert!(cloned.is::()); +//! ``` +//! +//! ## Patching +//! +//! These dynamic types come in handy when needing to apply multiple changes to another type. +//! This is known as "patching" and is done using the [`Reflect::apply`] method. +//! +//! ``` +//! # use bevy_reflect::{DynamicEnum, Reflect}; +//! let mut value = Some(123_i32); +//! let patch = DynamicEnum::new(std::any::type_name::>(), "None", ()); +//! value.apply(&patch); +//! assert_eq!(None, value); +//! ``` +//! +//! ## `FromReflect` +//! +//! It's important to remember that dynamics are _not_ the concrete type they may be representing. +//! A common mistake is to treat them like such when trying to cast back to the original type +//! or when trying to make use of a reflected trait which expects the actual type. +//! +//! ```should_panic +//! # use bevy_reflect::{DynamicStruct, Reflect}; +//! # #[derive(Reflect)] +//! # struct MyStruct { +//! # foo: i32 +//! # } +//! let original: Box = Box::new(MyStruct { +//! foo: 123 +//! }); +//! +//! let cloned: Box = original.clone_value(); +//! let value = cloned.take::().unwrap(); // PANIC! +//! ``` +//! +//! To resolve this issue, we'll need to convert the dynamic type to the concrete one. +//! This is where [`FromReflect`] comes in. +//! +//! `FromReflect` is a derivable trait that allows an instance of a type to be generated from a +//! dynamic representation— even partial ones. +//! And since the [`FromReflect::from_reflect`] method takes the data by reference, +//! this can be used to effectively clone data (to an extent). +//! +//! This trait can be derived on any type whose fields and sub-elements also implement `FromReflect`. +//! +//! ``` +//! # use bevy_reflect::{Reflect, FromReflect}; +//! #[derive(Reflect, FromReflect)] +//! struct MyStruct { +//! foo: i32 +//! } +//! let original: Box = Box::new(MyStruct { +//! foo: 123 +//! }); +//! +//! let cloned: Box = original.clone_value(); +//! let value = ::from_reflect(&*cloned).unwrap(); // OK! +//! ``` +//! +//! With the derive macro, fields can be ignored or given default values for when a field is missing +//! in the passed value. +//! See the [derive macro documentation](bevy_reflect_derive::FromReflect) for details. +//! +//! All primitives and simple types implement `FromReflect` by relying on their [`Default`] implementation. +//! +//! # Type Registration +//! +//! This crate also comes with a [`TypeRegistry`] that can be used to store and retrieve additional data, +//! such as helper types and trait implementations. +//! +//! The [derive macro] for [`Reflect`] also generates an implementation of the [`GetTypeRegistration`] trait, +//! which is used by the registry to generate a [`TypeRegistration`] struct for that type. +//! We can then register additional [type data] we want associated with that type. +//! +//! For example, we can register [`ReflectDefault`] on our type so that its `Default` implementation +//! may be used dynamically. +//! +//! ``` +//! # use bevy_reflect::{Reflect, TypeRegistry, prelude::ReflectDefault}; +//! #[derive(Reflect, Default)] +//! struct MyStruct { +//! foo: i32 +//! } +//! let mut registry = TypeRegistry::empty(); +//! registry.register::(); +//! registry.register_type_data::(); +//! +//! let registration = registry.get(std::any::TypeId::of::()).unwrap(); +//! let reflect_default = registration.data::().unwrap(); +//! +//! let new_value: Box = reflect_default.default(); +//! assert!(new_value.is::()); +//! ``` +//! +//! Because this operation is so common, the derive macro actually has a shorthand for it. +//! By using the `#[reflect(Trait)]` attribute, the derive macro will automatically register a matching, +//! in-scope `ReflectTrait` type within the `GetTypeRegistration` implementation. +//! +//! ``` +//! use bevy_reflect::prelude::{Reflect, ReflectDefault}; +//! +//! #[derive(Reflect, Default)] +//! #[reflect(Default)] +//! struct MyStruct { +//! foo: i32 +//! } +//! ``` +//! +//! ## Reflecting Traits +//! +//! Type data doesn't have to be tied to a trait, but they it's often extremely useful to create trait type data. +//! These allow traits to be used directly on a `dyn Reflect` while utilizing the underlying type's implementation. +//! +//! For any [object-safe] trait, we can easily generate a corresponding `ReflectTrait` type for our trait +//! using the [`reflect_trait`] macro. +//! +//! ``` +//! # use bevy_reflect::{Reflect, reflect_trait, TypeRegistry}; +//! #[reflect_trait] // Generates a `ReflectMyTrait` type +//! pub trait MyTrait {} +//! impl MyTrait for T {} +//! +//! let mut registry = TypeRegistry::new(); +//! registry.register_type_data::(); +//! ``` +//! +//! The generated type data can be used to convert a valid `dyn Reflect` into a `dyn MyTrait`. +//! See the [trait reflection example](https://github.com/bevyengine/bevy/blob/latest/examples/reflection/trait_reflection.rs) +//! for more information and usage details. +//! +//! # Serialization +//! +//! By using reflection, we are also able to get serialization capabilities for free. +//! In fact, using [`bevy_reflect`] can result in faster compile times and reduced code generation over +//! directly deriving the [`serde`] traits. +//! +//! The way it works is by moving the serialization logic into common serializers and deserializers: +//! * [`ReflectSerializer`] +//! * [`TypedReflectSerializer`] +//! * [`UntypedReflectDeserializer`] +//! * [`TypedReflectDeserializer`] +//! +//! All of these structs require a reference to the [registry] so that [type information] can be retrieved, +//! as well as registered type data, such as [`ReflectSerialize`] and [`ReflectDeserialize`]. +//! +//! The general entry point are the "untyped" versions of these structs. +//! These will automatically extract the type information and pass them into their respective "typed" version. +//! +//! The output of the `ReflectSerializer` will be a map, where the key is the [type name] +//! and the value is the serialized data. +//! The `TypedReflectSerializer` will simply output the serialized data. +//! +//! The `UntypedReflectDeserializer` can be used to deserialize this map and return a `Box`, +//! where the underlying type will be a dynamic representing some concrete type (except for value types). +//! +//! Again, it's important to remember that dynamics may need to be converted to their concrete counterparts +//! in order to be used in certain cases. +//! This can be achieved using [`FromReflect`]. +//! +//! ``` +//! # use serde::de::DeserializeSeed; +//! # use bevy_reflect::{ +//! # serde::{ReflectSerializer, UntypedReflectDeserializer}, +//! # Reflect, FromReflect, TypeRegistry +//! # }; +//! #[derive(Reflect, FromReflect, PartialEq, Debug)] +//! struct MyStruct { +//! foo: i32 +//! } +//! +//! let original_value = MyStruct { +//! foo: 123 +//! }; +//! +//! // Register +//! let mut registry = TypeRegistry::new(); +//! registry.register::(); +//! +//! // Serialize +//! let reflect_serializer = ReflectSerializer::new(&original_value, ®istry); +//! let serialized_value: String = ron::to_string(&reflect_serializer).unwrap(); +//! +//! // Deserialize +//! let reflect_deserializer = UntypedReflectDeserializer::new(®istry); +//! let deserialized_value: Box = reflect_deserializer.deserialize( +//! &mut ron::Deserializer::from_str(&serialized_value).unwrap() +//! ).unwrap(); +//! +//! // Convert +//! let converted_value = ::from_reflect(&*deserialized_value).unwrap(); +//! +//! assert_eq!(original_value, converted_value); +//! ``` +//! +//! [Reflection]: https://en.wikipedia.org/wiki/Reflective_programming +//! [Bevy]: https://bevyengine.org/ +//! [`bevy_reflect`]: crate +//! [runtime cost]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch +//! [derive macro]: bevy_reflect_derive::Reflect +//! [derive macro documentation]: bevy_reflect_derive::Reflect +//! [type data]: TypeData +//! [`ReflectDefault`]: std_traits::ReflectDefault +//! [object-safe]: https://doc.rust-lang.org/reference/items/traits.html#object-safety +//! [`serde`]: ::serde +//! [`ReflectSerializer`]: serde::ReflectSerializer +//! [`TypedReflectSerializer`]: serde::TypedReflectSerializer +//! [`UntypedReflectDeserializer`]: serde::UntypedReflectDeserializer +//! [`TypedReflectDeserializer`]: serde::TypedReflectDeserializer +//! [registry]: TypeRegistry +//! [type information]: TypeInfo +//! [type name]: Reflect::type_name mod array; mod fields; From fed2bf64180283ebd45aa47a5315d6b3994e984c Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 9 Jan 2023 23:50:50 -0700 Subject: [PATCH 02/15] Update docs for Reflect and FromReflect --- crates/bevy_reflect/src/from_reflect.rs | 25 ++++++++++++++++++------- crates/bevy_reflect/src/reflect.rs | 14 +++++++++----- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/crates/bevy_reflect/src/from_reflect.rs b/crates/bevy_reflect/src/from_reflect.rs index 41c4c3582bd3a..4d98698398bf0 100644 --- a/crates/bevy_reflect/src/from_reflect.rs +++ b/crates/bevy_reflect/src/from_reflect.rs @@ -1,15 +1,26 @@ use crate::{FromType, Reflect}; -/// A trait for types which can be constructed from a reflected type. +/// A trait that enables types to be dynamically constructed from reflected data. /// -/// This trait can be derived on types which implement [`Reflect`]. Some complex -/// types (such as `Vec`) may only be reflected if their element types +/// It's recommended to use the [derive macro] rather than manually implementing this trait. +/// +/// `FromReflect` allows dynamic proxy types, like [`DynamicStruct`], to be used to generate +/// their concrete counterparts. +/// It can also be used to partially or fully clone a type (depending on whether it has +/// ignored fields or not). +/// +/// In some cases, this trait may even be required. +/// Deriving [`Reflect`] on an enum requires all its fields to implement `FromReflect`. +/// Additionally, some complex types like `Vec` require that their element types /// implement this trait. +/// The reason for such requirements is that some operations require new data to be constructed, +/// such as swapping to a new variant or pushing data to a homogenous list. /// -/// For structs and tuple structs, fields marked with the `#[reflect(ignore)]` -/// attribute will be constructed using the `Default` implementation of the -/// field type, rather than the corresponding field value (if any) of the -/// reflected value. +/// See the [crate-level documentation] to see how this trait can be used. +/// +/// [derive macro]: bevy_reflect_derive::FromReflect +/// [`DynamicStruct`]: crate::DynamicStruct +/// [crate-level documentation]: crate pub trait FromReflect: Reflect + Sized { /// Constructs a concrete instance of `Self` from a reflected value. fn from_reflect(reflect: &dyn Reflect) -> Option; diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index 90c57f7d5bbd6..8abfeef742fbd 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -61,13 +61,17 @@ pub enum ReflectOwned { Value(Box), } -/// A reflected Rust type. +/// The core trait of [`bevy_reflect`], used for accessing and modifying data dynamically. /// -/// Methods for working with particular kinds of Rust type are available using the [`Array`], [`List`], -/// [`Map`], [`Tuple`], [`TupleStruct`], [`Struct`], and [`Enum`] subtraits. +/// It's recommended to use the [derive macro] rather than manually implementing this trait. +/// Doing so will automatically implement many other useful traits for reflection, +/// including one of the appropriate subtraits: [`Struct`], [`TupleStruct`] or [`Enum`]. /// -/// When using `#[derive(Reflect)]` on a struct, tuple struct or enum, the suitable subtrait for that -/// type (`Struct`, `TupleStruct` or `Enum`) is derived automatically. +/// See the [crate-level documentation] to see how this trait and its subtraits can be used. +/// +/// [`bevy_reflect`]: crate +/// [derive macro]: bevy_reflect_derive::Reflect +/// [crate-level documentation]: crate pub trait Reflect: Any + Send + Sync { /// Returns the [type name][std::any::type_name] of the underlying type. fn type_name(&self) -> &str; From efa81a8f354b9ce19381faa9ed64126e13814e2f Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 9 Jan 2023 23:51:18 -0700 Subject: [PATCH 03/15] Update docs for reflection subtraits --- crates/bevy_reflect/src/array.rs | 34 +++++++++++++++--- crates/bevy_reflect/src/enums/enum_trait.rs | 10 +++--- crates/bevy_reflect/src/list.rs | 38 +++++++++++++++++---- crates/bevy_reflect/src/map.rs | 37 ++++++++++++++++---- crates/bevy_reflect/src/struct_trait.rs | 28 ++++++++------- crates/bevy_reflect/src/tuple.rs | 18 ++++++---- crates/bevy_reflect/src/tuple_struct.rs | 26 ++++++++------ 7 files changed, 138 insertions(+), 53 deletions(-) diff --git a/crates/bevy_reflect/src/array.rs b/crates/bevy_reflect/src/array.rs index a7a6088fda177..6db8e887e8aca 100644 --- a/crates/bevy_reflect/src/array.rs +++ b/crates/bevy_reflect/src/array.rs @@ -8,14 +8,38 @@ use std::{ hash::{Hash, Hasher}, }; -/// A static-sized array of [`Reflect`] items. +/// A trait used to power [array-like] operations via [reflection]. /// -/// This corresponds to types like `[T; N]` (arrays). +/// This corresponds to true Rust arrays like `[T; N]`, +/// but also to any fixed-size linear sequence types. +/// It is expected that implementors of this trait uphold this contract +/// and maintain a fixed size as returned by the [`Array::len`] method. /// -/// Currently, this only supports arrays of up to 32 items. It can technically -/// contain more than 32, but the blanket [`GetTypeRegistration`] is only -/// implemented up to the 32 item limit due to a [limitation] on `Deserialize`. +/// Due to the [type-erasing] nature of the reflection API as a whole, +/// this trait does not make any guarantees that the implementor's elements +/// are homogenous (i.e. all the same type). /// +/// This trait has a blanket implementation over Rust arrays of up to 32 items. +/// This implementation can technically contain more than 32, +/// but the blanket [`GetTypeRegistration`] is only implemented up to the 32 +/// item limit due to a [limitation] on `Deserialize`. +/// +/// # Example +/// +/// ``` +/// use bevy_reflect::{Reflect, Array}; +/// +/// let foo: &dyn Array = &[123_u32, 456_u32, 789_u32]; +/// assert_eq!(foo.len(), 3); +/// +/// let field: &dyn Reflect = foo.get(0).unwrap(); +/// assert_eq!(field.downcast_ref::(), Some(&123)); +/// ``` +/// +/// [array-like]: https://doc.rust-lang.org/book/ch03-02-data-types.html#the-array-type +/// [reflection]: crate +/// [`List`]: crate::List +/// [type-erasing]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html /// [`GetTypeRegistration`]: crate::GetTypeRegistration /// [limitation]: https://github.com/serde-rs/serde/issues/1937 pub trait Array: Reflect { diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 5d5a04c91d53a..620575f5d415a 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -3,7 +3,7 @@ use bevy_utils::HashMap; use std::any::{Any, TypeId}; use std::slice::Iter; -/// A trait representing a [reflected] enum. +/// A trait used to power [enum-like] operations via [reflection]. /// /// This allows enums to be processed and modified dynamically at runtime without /// necessarily knowing the actual type. @@ -39,7 +39,7 @@ use std::slice::Iter; /// /// # Implementation /// -/// > 💡 This trait can be automatically implemented using the [`Reflect`] derive macro +/// > 💡 This trait can be automatically implemented using the [`Reflect` derive macro] /// > on an enum definition. /// /// Despite the fact that enums can represent multiple states, traits only exist in one state @@ -52,7 +52,7 @@ use std::slice::Iter; /// accessing fields! /// Again, this is to account for _all three_ variant types. /// -/// We recommend using the built-in [`Reflect`] derive macro to automatically handle all the +/// We recommend using the built-in [`Reflect` derive macro] to automatically handle all the /// implementation details for you. /// However, if you _must_ implement this trait manually, there are a few things to keep in mind... /// @@ -82,10 +82,12 @@ use std::slice::Iter; /// It's preferred that these strings be converted to their proper `usize` representations and /// the [`Enum::field_at[_mut]`](Enum::field_at) methods be used instead. /// -/// [reflected]: crate +/// [enum-like]: https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html +/// [reflection]: crate /// [`None`]: core::option::Option::None /// [`Some`]: core::option::Option::Some /// [`Reflect`]: bevy_reflect_derive::Reflect +/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect pub trait Enum: Reflect { /// Returns a reference to the value of the field (in the current variant) with the given name. /// diff --git a/crates/bevy_reflect/src/list.rs b/crates/bevy_reflect/src/list.rs index 5cbc21cb329b4..6e9154fa99a77 100644 --- a/crates/bevy_reflect/src/list.rs +++ b/crates/bevy_reflect/src/list.rs @@ -7,19 +7,43 @@ use crate::{ DynamicInfo, FromReflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, Typed, }; -/// An ordered, mutable list of [Reflect] items. This corresponds to types like [`std::vec::Vec`]. +/// A trait used to power [list-like] operations via [reflection]. /// -/// Unlike the [`Array`](crate::Array) trait, implementors of this type are not expected to +/// This corresponds to types, like [`Vec`], which contain an ordered sequence +/// of elements that implement [`Reflect`]. +/// +/// Unlike the [`Array`](crate::Array) trait, implementors of this trait are not expected to /// maintain a constant length. /// Methods like [insertion](List::insert) and [removal](List::remove) explicitly allow for their /// internal size to change. /// -/// This trait expects index 0 to contain the _front_ element. -/// The _back_ element must refer to the element with the largest index. -/// These two rules above should be upheld by manual implementors. -/// /// [`push`](List::push) and [`pop`](List::pop) have default implementations, -/// however it may be faster to implement them manually. +/// however it will generally be more performant to implement them manually. +/// +/// This trait expects its elements to be ordered linearly from front to back. +/// The _front_ element starts at index 0 with the _back_ element ending at the largest index. +/// This contract above should be upheld by any manual implementors. +/// +/// Due to the [type-erasing] nature of the reflection API as a whole, +/// this trait does not make any guarantees that the implementor's elements +/// are homogenous (i.e. all the same type). +/// +/// # Example +/// +/// ``` +/// use bevy_reflect::{Reflect, List}; +/// +/// let foo: &mut dyn List = &mut vec![123_u32, 456_u32, 789_u32]; +/// assert_eq!(foo.len(), 3); +/// +/// let last_field: Box = foo.pop().unwrap(); +/// assert_eq!(last_field.downcast_ref::(), Some(&789)); +/// ``` +/// +/// [list-like]: https://doc.rust-lang.org/book/ch08-01-vectors.html +/// [reflection]: crate +/// [`Vec`]: std::vec::Vec +/// [type-erasing]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html pub trait List: Reflect { /// Returns a reference to the element at `index`, or `None` if out of bounds. fn get(&self, index: usize) -> Option<&dyn Reflect>; diff --git a/crates/bevy_reflect/src/map.rs b/crates/bevy_reflect/src/map.rs index f5e4a8695f2a2..70082330e9df4 100644 --- a/crates/bevy_reflect/src/map.rs +++ b/crates/bevy_reflect/src/map.rs @@ -7,16 +7,39 @@ use bevy_utils::{Entry, HashMap}; use crate::utility::NonGenericTypeInfoCell; use crate::{DynamicInfo, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, Typed}; -/// An ordered mapping between [`Reflect`] values. +/// A trait used to power [map-like] operations via [reflection]. /// -/// Because the values are reflected, the underlying types of keys and values -/// may differ between entries. +/// Maps contain zero or more entries of a key and its associated value, +/// and correspond to types like [`HashMap`]. +/// The order of these entries is not guaranteed by this trait. /// -///`ReflectValue` `Keys` are assumed to return a non-`None` hash. The ordering -/// of `Map` entries is not guaranteed to be stable across runs or between -/// instances. +/// # Hashing /// -/// This trait corresponds to types like [`std::collections::HashMap`]. +/// All keys are expected to return a valid hash value from [`Reflect::reflect_hash`]. +/// If using the [`Reflect` derive macro], this can be done by adding `#[reflect(Hash)]` +/// to the entire struct or enum. +/// This is true even for manual implementors who do not use the hashed value, +/// as it is still relied on by [`DynamicMap`]. +/// +/// # Example +/// +/// ``` +/// use bevy_reflect::{Reflect, Map}; +/// use bevy_utils::HashMap; +/// +/// +/// let foo: &mut dyn Map = &mut HashMap::::new(); +/// foo.insert_boxed(Box::new(123_u32), Box::new(true)); +/// assert_eq!(foo.len(), 1); +/// +/// let field: &dyn Reflect = foo.get(&123_u32).unwrap(); +/// assert_eq!(field.downcast_ref::(), Some(&true)); +/// ``` +/// +/// [map-like]: https://doc.rust-lang.org/book/ch08-03-hash-maps.html +/// [reflection]: crate +/// [`HashMap`]: bevy_utils::HashMap +/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect pub trait Map: Reflect { /// Returns a reference to the value associated with the given key. /// diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index 4b8485cbb5b3e..0245af9b09694 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -10,13 +10,14 @@ use std::{ slice::Iter, }; -/// A reflected Rust regular struct type. +/// A trait used to power [struct-like] operations via [reflection]. /// -/// Implementors of this trait allow their fields to be addressed by name as -/// well as by index. +/// This trait uses the [`Reflect`] trait to allow implementors to have their fields +/// be dynamically addressed by both name and index. /// -/// This trait is automatically implemented for `struct` types with named fields -/// when using `#[derive(Reflect)]`. +/// When using the [`Reflect` derive macro] on a standard struct, +/// this trait will be automatically implemented. +/// This goes for [unit structs] as well. /// /// # Example /// @@ -25,19 +26,22 @@ use std::{ /// /// #[derive(Reflect)] /// struct Foo { -/// bar: String, +/// bar: u32, /// } /// -/// # fn main() { -/// let foo = Foo { bar: "Hello, world!".to_string() }; +/// let foo = Foo { bar: 123 }; /// /// assert_eq!(foo.field_len(), 1); /// assert_eq!(foo.name_at(0), Some("bar")); /// -/// let bar = foo.field("bar").unwrap(); -/// assert_eq!(bar.downcast_ref::(), Some(&"Hello, world!".to_string())); -/// # } +/// let field: &dyn Reflect = foo.field("bar").unwrap(); +/// assert_eq!(field.downcast_ref::(), Some(&123)); /// ``` +/// +/// [struct-like]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html +/// [reflection]: crate +/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect +/// [unit structs]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html#unit-like-structs-without-any-fields pub trait Struct: Reflect { /// Returns a reference to the value of the field named `name` as a `&dyn /// Reflect`. @@ -68,7 +72,7 @@ pub trait Struct: Reflect { fn clone_dynamic(&self) -> DynamicStruct; } -/// A container for compile-time struct info. +/// A container for compile-time named struct info. #[derive(Clone, Debug)] pub struct StructInfo { name: &'static str, diff --git a/crates/bevy_reflect/src/tuple.rs b/crates/bevy_reflect/src/tuple.rs index cebabaf3ffc9d..f0e20aff6ba25 100644 --- a/crates/bevy_reflect/src/tuple.rs +++ b/crates/bevy_reflect/src/tuple.rs @@ -7,7 +7,10 @@ use std::any::{Any, TypeId}; use std::fmt::{Debug, Formatter}; use std::slice::Iter; -/// A reflected Rust tuple. +/// A trait used to power [tuple-like] operations via [reflection]. +/// +/// This trait uses the [`Reflect`] trait to allow implementors to have their fields +/// be dynamically addressed by index. /// /// This trait is automatically implemented for arbitrary tuples of up to 12 /// elements, provided that each element implements [`Reflect`]. @@ -15,16 +18,17 @@ use std::slice::Iter; /// # Example /// /// ``` -/// use bevy_reflect::Tuple; +/// use bevy_reflect::{Reflect, Tuple}; /// -/// # fn main() { -/// let foo = ("blue".to_string(), 42_i32); +/// let foo = (123_u32, true); /// assert_eq!(foo.field_len(), 2); /// -/// let first = foo.field(0).unwrap(); -/// assert_eq!(first.downcast_ref::(), Some(&"blue".to_string())); -/// # } +/// let field: &dyn Reflect = foo.field(0).unwrap(); +/// assert_eq!(field.downcast_ref::(), Some(&123)); /// ``` +/// +/// [tuple-like]: https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type +/// [reflection]: crate pub trait Tuple: Reflect { /// Returns a reference to the value of the field with index `index` as a /// `&dyn Reflect`. diff --git a/crates/bevy_reflect/src/tuple_struct.rs b/crates/bevy_reflect/src/tuple_struct.rs index e8c52bdadddbd..22c5e200486f6 100644 --- a/crates/bevy_reflect/src/tuple_struct.rs +++ b/crates/bevy_reflect/src/tuple_struct.rs @@ -6,29 +6,33 @@ use std::any::{Any, TypeId}; use std::fmt::{Debug, Formatter}; use std::slice::Iter; -/// A reflected Rust tuple struct. +/// A trait used to power [tuple struct-like] operations via [reflection]. /// -/// Implementors of this trait allow their tuple fields to be addressed by -/// index. +/// This trait uses the [`Reflect`] trait to allow implementors to have their fields +/// be dynamically addressed by index. /// -/// This trait is automatically implemented for tuple struct types when using -/// `#[derive(Reflect)]`. +/// When using the [`Reflect` derive macro] on a tuple struct, +/// this trait will be automatically implemented. +/// +/// # Example /// /// ``` /// use bevy_reflect::{Reflect, TupleStruct}; /// /// #[derive(Reflect)] -/// struct Foo(String); +/// struct Foo(u32); /// -/// # fn main() { -/// let foo = Foo("Hello, world!".to_string()); +/// let foo = Foo(123); /// /// assert_eq!(foo.field_len(), 1); /// -/// let first = foo.field(0).unwrap(); -/// assert_eq!(first.downcast_ref::(), Some(&"Hello, world!".to_string())); -/// # } +/// let field: &dyn Reflect = foo.field(0).unwrap(); +/// assert_eq!(field.downcast_ref::(), Some(&123)); /// ``` +/// +/// [tuple struct-like]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html#using-tuple-structs-without-named-fields-to-create-different-types +/// [reflection]: crate +/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect pub trait TupleStruct: Reflect { /// Returns a reference to the value of the field with index `index` as a /// `&dyn Reflect`. From 87dc78ef726f5a3e7b25509804f016b41f9d0a42 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 9 Jan 2023 23:51:33 -0700 Subject: [PATCH 04/15] Update docs for other reflection types --- crates/bevy_reflect/src/type_info.rs | 3 ++- crates/bevy_reflect/src/type_registry.rs | 14 +++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/src/type_info.rs b/crates/bevy_reflect/src/type_info.rs index 42e011c14915f..3e8336be9bfe9 100644 --- a/crates/bevy_reflect/src/type_info.rs +++ b/crates/bevy_reflect/src/type_info.rs @@ -5,7 +5,7 @@ use std::any::{Any, TypeId}; /// A static accessor to compile-time type information. /// -/// This trait is automatically implemented by the `#[derive(Reflect)]` macro +/// This trait is automatically implemented by the [`Reflect` derive macro] /// and allows type information to be processed without an instance of that type. /// /// # Implementing @@ -65,6 +65,7 @@ use std::any::{Any, TypeId}; /// # } /// ``` /// +/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect /// [utility]: crate::utility pub trait Typed: Reflect { /// Returns the compile-time [info] for the underlying type. diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 2a6fc6f8168be..57bb841620047 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -6,7 +6,19 @@ use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use serde::Deserialize; use std::{any::TypeId, fmt::Debug, sync::Arc}; -/// A registry of reflected types. +/// A registry of [reflected] types. +/// +/// This struct is used as the central store for type information. +/// [Registering] a type will generate a new [`TypeRegistration`] entry in this store +/// using a type's [`GetTypeRegistration`] implementation +/// (which will is automatically implemented when using the [`Reflect` derive macro]). +/// +/// See the [crate-level documentation] for more information. +/// +/// [reflected]: crate +/// [Registering]: TypeRegistry::register +/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect +/// [crate-level documentation]: crate pub struct TypeRegistry { registrations: HashMap, short_name_to_id: HashMap, From e799663f28f162ffe258b42c83ad23f22ce0ddc7 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 9 Jan 2023 23:52:00 -0700 Subject: [PATCH 05/15] Update docs for reflection macros --- .../bevy_reflect_derive/src/lib.rs | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 4d3c27caecb9b..274c7b2874610 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -41,7 +41,84 @@ use type_uuid::TypeUuidDef; pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; -#[proc_macro_derive(Reflect, attributes(reflect, reflect_value, module))] +/// The main derive macro used by `bevy_reflect` for deriving its `Reflect` trait. +/// +/// This macro can be used on all structs and enums (unions are not supported). +/// It will automatically generate the implementations for `Reflect`, `Typed`, and `GetTypeRegistration`. +/// And, depending on the item's structure, will either implement `Struct`, `TupleStruct`, or `Enum`. +/// +/// # Container Attributes +/// +/// This macro comes with some helper attributes that can be added to the container item +/// in order to provide additional functionality or alter the generated implementations. +/// +/// ## `#[reflect(Ident)]` +/// +/// The `#[reflect(Ident)]` attribute is used to add type data registrations to the `GetTypeRegistration` +/// implementation corresponding to the given identifier, prepended by `Reflect`. +/// +/// For example, `#[reflect(Foo, Bar)]` would add two registrations: +/// one for `ReflectFoo` and another for `ReflectBar`. +/// This assumes these types are indeed in-scope wherever this macro is called. +/// +/// ### Special Identifiers +/// +/// There are a few "special" identifiers that work a bit differently: +/// +/// * `#[reflect(Debug)]` will force the implementation of `Reflect::reflect_debug` to rely on +/// the type's [`Debug`] implementation. +/// A custom implementation may be provided using `#[reflect(Debug(my_debug_func))]` where +/// `my_debug_func` is the path to a function matching the signature: +/// `(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result`. +/// * `#[reflect(PartialEq)]` will force the implementation of `Reflect::reflect_partial_eq` to rely on +/// the type's [`PartialEq`] implementation. +/// A custom implementation may be provided using `#[reflect(PartialEq(my_partial_eq_func))]` where +/// `my_partial_eq_func` is the path to a function matching the signature: +/// `(&self, value: &dyn #bevy_reflect_path::Reflect) -> bool`. +/// * `#[reflect(Hash)]` will force the implementation of `Reflect::reflect_hash` to rely on +/// the type's [`Hash`] implementation. +/// A custom implementation may be provided using `#[reflect(Hash(my_hash_func))]` where +/// `my_hash_func` is the path to a function matching the signature: `(&self) -> u64`. +/// * `#[reflect(Default)]` will register the `ReflectDefault` type data as normal. +/// However, it will also affect how certain other operations are performed in order +/// to improve performance and/or robustness. +/// An example of where this is used is in the [`FromReflect`] derive macro, +/// where adding this attribute will cause the `FromReflect` implementation to create +/// a base value using its [`Default`] implementation avoiding issues with ignored fields. +/// +/// ## `#[reflect_value]` +/// +/// The `#[reflect_value]` attribute (which may also take the form `#[reflect_value(Ident)]`), +/// denotes that the item should implement `Reflect` as though it were a base value type. +/// This means that it will forgo implementing `Struct`, `TupleStruct`, or `Enum`. +/// +/// Furthermore, it requires that the type implements [`Clone`]. +/// If planning to serialize this type using the reflection serializers, +/// then the `Serialize` and `Deserialize` traits will need to be implemented and registered as well. +/// +/// # Field Attributes +/// +/// Along with the container attributes, this macro comes with some attributes that may be applied +/// to the contained fields themselves. +/// +/// ## `#[reflect(ignore)]` +/// +/// This attribute simply marks a field to be ignored by the reflection API. +/// +/// This allows fields to completely opt-out of reflection, +/// which may be useful for maintaining invariants, keeping certain data private, +/// or allowing the use of types that do not implement `Reflect` within the container. +/// +/// ## `#[reflect(skip_serializing)]` +/// +/// This works similar to `#[reflect(ignore)]`, but rather than opting out of _all_ of reflection, +/// it simply opts the field out of serialization. +/// +/// This is done by registering the `SerializationData` type within the `GetTypeRegistration` implementation, +/// which will be used by the reflection serializers to determine whether or not the field is serializable. +/// +/// Note that this affects both serialization and deserialization. +#[proc_macro_derive(Reflect, attributes(reflect, reflect_value))] pub fn derive_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); From 6f1f6acdbe1b19ed66f8de4fd687a6072f232a9a Mon Sep 17 00:00:00 2001 From: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Date: Fri, 27 Jan 2023 22:14:35 -0700 Subject: [PATCH 06/15] Apply suggestions from code review Co-authored-by: Alice Cecile Co-authored-by: Nick Fagerlund --- crates/bevy_reflect/src/array.rs | 3 ++- crates/bevy_reflect/src/lib.rs | 18 +++++++++--------- crates/bevy_reflect/src/list.rs | 3 ++- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/crates/bevy_reflect/src/array.rs b/crates/bevy_reflect/src/array.rs index 6db8e887e8aca..947601a1548e5 100644 --- a/crates/bevy_reflect/src/array.rs +++ b/crates/bevy_reflect/src/array.rs @@ -22,7 +22,7 @@ use std::{ /// This trait has a blanket implementation over Rust arrays of up to 32 items. /// This implementation can technically contain more than 32, /// but the blanket [`GetTypeRegistration`] is only implemented up to the 32 -/// item limit due to a [limitation] on `Deserialize`. +/// item limit due to a [limitation] on [`Deserialize`]. /// /// # Example /// @@ -42,6 +42,7 @@ use std::{ /// [type-erasing]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html /// [`GetTypeRegistration`]: crate::GetTypeRegistration /// [limitation]: https://github.com/serde-rs/serde/issues/1937 +/// [`Deserialize`]: ::serde::Deserialize pub trait Array: Reflect { /// Returns a reference to the element at `index`, or `None` if out of bounds. fn get(&self, index: usize) -> Option<&dyn Reflect>; diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index aec509beb07bf..88c83a6292307 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -8,7 +8,7 @@ //! //! While Rust doesn't yet provide much built-in tooling for reflection, //! this crate serves to help fill that role. -//! And while it was made with the [Bevy] game engine in mind, +//! Though it was made with the [Bevy] game engine in mind, //! it's a general-purpose solution that can be used in any Rust project. //! //! At a very high level, this crate allows you to: @@ -45,7 +45,7 @@ //! //! We can implement `Reflect` on any type that satisfies _both_ of the following conditions: //! * The type implements `Any`. -//! In other words, the type itself has a `'static` lifetime. +//! This is true if and only if the type itself has a `'static` lifetime. //! * All fields and sub-elements themselves implement `Reflect` //! (see the [derive macro documentation] for details on how to ignore certain fields when deriving). //! @@ -101,7 +101,7 @@ //! ``` //! //! And to go back to a general-purpose `dyn Reflect`, -//! we can just use of the matching [`Reflect::as_reflect`], [`Reflect::as_reflect_mut`], +//! we can just use the matching [`Reflect::as_reflect`], [`Reflect::as_reflect_mut`], //! or [`Reflect::into_reflect`] methods. //! //! ## Value Types @@ -138,7 +138,7 @@ //! //! They are most commonly used as "proxies" for other types, //! where they contain the same data as— and therefore, represent— a concrete type. -//! The [`Reflect::clone_value`] will return a dynamic type for all non-value types, +//! The [`Reflect::clone_value`] method will return a dynamic type for all non-value types, //! allowing all types to essentially be "cloned". //! And since dynamic types themselves implement [`Reflect`], //! we may pass them around just like any other reflected type. @@ -174,7 +174,7 @@ //! //! ## `FromReflect` //! -//! It's important to remember that dynamics are _not_ the concrete type they may be representing. +//! It's important to remember that dynamic types are _not_ the concrete type they may be representing. //! A common mistake is to treat them like such when trying to cast back to the original type //! or when trying to make use of a reflected trait which expects the actual type. //! @@ -224,7 +224,7 @@ //! //! # Type Registration //! -//! This crate also comes with a [`TypeRegistry`] that can be used to store and retrieve additional data, +//! This crate also comes with a [`TypeRegistry`] that can be used to store and retrieve additional type metadata at runtime, //! such as helper types and trait implementations. //! //! The [derive macro] for [`Reflect`] also generates an implementation of the [`GetTypeRegistration`] trait, @@ -267,7 +267,7 @@ //! //! ## Reflecting Traits //! -//! Type data doesn't have to be tied to a trait, but they it's often extremely useful to create trait type data. +//! Type data doesn't have to be tied to a trait, but it's often extremely useful to create trait type data. //! These allow traits to be used directly on a `dyn Reflect` while utilizing the underlying type's implementation. //! //! For any [object-safe] trait, we can easily generate a corresponding `ReflectTrait` type for our trait @@ -310,9 +310,9 @@ //! The `TypedReflectSerializer` will simply output the serialized data. //! //! The `UntypedReflectDeserializer` can be used to deserialize this map and return a `Box`, -//! where the underlying type will be a dynamic representing some concrete type (except for value types). +//! where the underlying type will be a dynamic type representing some concrete type (except for value types). //! -//! Again, it's important to remember that dynamics may need to be converted to their concrete counterparts +//! Again, it's important to remember that dynamic types may need to be converted to their concrete counterparts //! in order to be used in certain cases. //! This can be achieved using [`FromReflect`]. //! diff --git a/crates/bevy_reflect/src/list.rs b/crates/bevy_reflect/src/list.rs index 6e9154fa99a77..d13e7776f0693 100644 --- a/crates/bevy_reflect/src/list.rs +++ b/crates/bevy_reflect/src/list.rs @@ -18,7 +18,8 @@ use crate::{ /// internal size to change. /// /// [`push`](List::push) and [`pop`](List::pop) have default implementations, -/// however it will generally be more performant to implement them manually. +/// however it will generally be more performant to implement them manually +/// as the default implementation uses a very naive approach to find the correct position. /// /// This trait expects its elements to be ordered linearly from front to back. /// The _front_ element starts at index 0 with the _back_ element ending at the largest index. From d1684bb951bce994429146a6c24873436d20fe2b Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Fri, 27 Jan 2023 22:37:11 -0700 Subject: [PATCH 07/15] Add more detail based on feedback --- crates/bevy_reflect/bevy_reflect_derive/src/lib.rs | 8 ++++---- crates/bevy_reflect/src/lib.rs | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 274c7b2874610..b38d6bef7a809 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -112,12 +112,12 @@ pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; /// ## `#[reflect(skip_serializing)]` /// /// This works similar to `#[reflect(ignore)]`, but rather than opting out of _all_ of reflection, -/// it simply opts the field out of serialization. +/// it simply opts the field out of both serialization and deserialization. +/// This can be useful when a field should be accessible via reflection, but may not make +/// sense in a serialized form, such as computed data. /// -/// This is done by registering the `SerializationData` type within the `GetTypeRegistration` implementation, +/// What this does is register the `SerializationData` type within the `GetTypeRegistration` implementation, /// which will be used by the reflection serializers to determine whether or not the field is serializable. -/// -/// Note that this affects both serialization and deserialization. #[proc_macro_derive(Reflect, attributes(reflect, reflect_value))] pub fn derive_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 88c83a6292307..591a01a577f56 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -6,8 +6,7 @@ //! In other words, reflection allows us to inspect the program itself, its //! syntax, and its type information at runtime. //! -//! While Rust doesn't yet provide much built-in tooling for reflection, -//! this crate serves to help fill that role. +//! This crate adds this missing reflection functionality to Rust. //! Though it was made with the [Bevy] game engine in mind, //! it's a general-purpose solution that can be used in any Rust project. //! @@ -45,7 +44,7 @@ //! //! We can implement `Reflect` on any type that satisfies _both_ of the following conditions: //! * The type implements `Any`. -//! This is true if and only if the type itself has a `'static` lifetime. +//! This is true if and only if the type itself has a [`'static` lifetime]. //! * All fields and sub-elements themselves implement `Reflect` //! (see the [derive macro documentation] for details on how to ignore certain fields when deriving). //! @@ -356,6 +355,7 @@ //! [`bevy_reflect`]: crate //! [runtime cost]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch //! [derive macro]: bevy_reflect_derive::Reflect +//! [`'static` lifetime]: https://doc.rust-lang.org/rust-by-example/scope/lifetime/static_lifetime.html#trait-bound //! [derive macro documentation]: bevy_reflect_derive::Reflect //! [type data]: TypeData //! [`ReflectDefault`]: std_traits::ReflectDefault From 82c3d134dc8197d7176cc5118c151d3ceb5d51e1 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Fri, 27 Jan 2023 23:16:23 -0700 Subject: [PATCH 08/15] Document `reflect_trait` --- .../bevy_reflect_derive/src/lib.rs | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index b38d6bef7a809..215d3f4c680ee 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -61,6 +61,9 @@ pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; /// one for `ReflectFoo` and another for `ReflectBar`. /// This assumes these types are indeed in-scope wherever this macro is called. /// +/// This is often used with traits that have been marked by the [`reflect_trait`] macro +/// in order to register the type's implementation of that trait. +/// /// ### Special Identifiers /// /// There are a few "special" identifiers that work a bit differently: @@ -118,6 +121,8 @@ pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; /// /// What this does is register the `SerializationData` type within the `GetTypeRegistration` implementation, /// which will be used by the reflection serializers to determine whether or not the field is serializable. +/// +/// [`reflect_trait`]: macro@reflect_trait #[proc_macro_derive(Reflect, attributes(reflect, reflect_value))] pub fn derive_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); @@ -169,6 +174,55 @@ pub fn derive_type_uuid(input: TokenStream) -> TokenStream { type_uuid::type_uuid_derive(input) } +/// A macro that automatically generates type data for traits, which their implementors can then register. +/// +/// The output of this macro is a struct that takes reflected instances of the implementor's type +/// and returns the value as a trait object. +/// Because of this, **it can only be used on [object-safe] traits.** +/// +/// For a trait named `MyTrait`, this will generate the struct `ReflectMyTrait`. +/// The generated struct can be created using `FromType` with any type that implements the trait. +/// The creation and registration of this generated struct as type data can be automatically handled +/// by the [`Reflect` derive macro]. +/// +/// # Example +/// +/// ```ignore +/// # use std::any::TypeId; +/// # use bevy_reflect_derive::{Reflect, reflect_trait}; +/// #[reflect_trait] // Generates `ReflectMyTrait` +/// trait MyTrait { +/// fn print(&self) -> &str; +/// } +/// +/// #[derive(Reflect)] +/// #[reflect(MyTrait)] // Automatically registers `ReflectMyTrait` +/// struct SomeStruct; +/// +/// impl MyTrait for SomeStruct { +/// fn print(&self) -> &str { +/// "Hello, World!" +/// } +/// } +/// +/// // We can create the type data manually if we wanted: +/// let my_trait: ReflectMyTrait = FromType::::from_type(); +/// +/// // Or we can simply get it from the registry: +/// let mut registry = TypeRegistry::default(); +/// registry.register::(); +/// let my_trait = registry +/// .get_type_data::(TypeId::of::()) +/// .unwrap(); +/// +/// // Then use it on reflected data +/// let reflected: Box = Box::new(SomeStruct); +/// let reflected_my_trait: &dyn MyTrait = my_trait.get(&*reflected).unwrap(); +/// assert_eq!("Hello, World!", reflected_my_trait.print()); +/// ``` +/// +/// [object-safe]: https://doc.rust-lang.org/reference/items/traits.html#object-safety +/// [`Reflect` derive macro]: Reflect #[proc_macro_attribute] pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { trait_reflection::reflect_trait(&args, input) From dcf3b933b65fe919cfcc370a12923046a4b30ece Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Fri, 27 Jan 2023 23:48:02 -0700 Subject: [PATCH 09/15] Update documentation on `FromReflect` derive --- .../bevy_reflect_derive/src/lib.rs | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 215d3f4c680ee..3b2ebcb80a77b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -144,11 +144,32 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { /// Derives the `FromReflect` trait. /// -/// This macro supports the following field attributes: -/// * `#[reflect(ignore)]`: Ignores the field. This requires the field to implement [`Default`]. -/// * `#[reflect(default)]`: If the field's value cannot be read, uses its [`Default`] implementation. -/// * `#[reflect(default = "some_func")]`: If the field's value cannot be read, uses the function with the given name. +/// # Field Attributes +/// +/// ## `#[reflect(ignore)]` +/// +/// The `#[reflect(ignore)]` attribute is shared with the [`Reflect` derive macro] and has much of the same +/// functionality in that it marks a field to be ignored by the reflection API. +/// +/// The only major difference is that using it with this derive requires that the field implements [`Default`]. +/// Without this requirement, there would be no way for `FromReflect` to automatically construct missing fields +/// that have been ignored. /// +/// ## `#[reflect(default)]` +/// +/// If a field cannot be read, this attribute specifies a default value to be used in its place. +/// +/// By default, this attribute denotes that the field's type implements [`Default`]. +/// However, it can also take in a path string to a user-defined function that will return the default value. +/// This takes the form: `#[reflect(default = "path::to::my_function)]` where `my_function` is a parameterless +/// function that must return some default value for the type. +/// +/// Specifying a custom default can be used to give different fields their own specialized defaults, +/// or to remove the `Default` requirement on fields marked with `#[reflect(ignore)]`. +/// Additionally, either form of this attribute can be used to fill in fields that are simply missing, +/// such as when converting a partially-constructed dynamic type to a concrete one. +/// +/// [`Reflect` derive macro]: Reflect #[proc_macro_derive(FromReflect, attributes(reflect))] pub fn derive_from_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); From a466a90c01b7331288b2ad29c15a26639db8ee4b Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 28 Jan 2023 00:06:48 -0700 Subject: [PATCH 10/15] Document `impl_reflect_value` and `impl_from_reflect_value` --- .../bevy_reflect_derive/src/lib.rs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 3b2ebcb80a77b..b17808db8538d 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -249,6 +249,29 @@ pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { trait_reflection::reflect_trait(&args, input) } +/// A macro used to generate reflection trait implementations for the given type. +/// +/// This is functionally the same as [deriving `Reflect`] using the `#[reflect_value]` container attribute. +/// +/// The only reason for this macro's existence is so that `bevy_reflect` can easily implement the reflection traits +/// on primitives and other Rust types internally. +/// +/// # Examples +/// +/// Types can be passed with or without registering type data: +/// +/// ```ignore +/// impl_reflect_value!(foo); +/// impl_reflect_value!(bar(Debug, Default, Serialize, Deserialize)); +/// ``` +/// +/// Generic types can also specify their parameters and bounds: +/// +/// ```ignore +/// impl_reflect_value!(foo where T1: Bar (Default, Serialize, Deserialize)); +/// ``` +/// +/// [deriving `Reflect`]: Reflect #[proc_macro] pub fn impl_reflect_value(input: TokenStream) -> TokenStream { let def = parse_macro_input!(input as ReflectValueDef); @@ -330,6 +353,22 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { } } +/// A macro used to generate a `FromReflect` trait implementation for the given type. +/// +/// This is functionally the same as [deriving `FromReflect`] on a type that [derives `Reflect`] using +/// the `#[reflect_value]` container attribute. +/// +/// The only reason this macro exists is so that `bevy_reflect` can easily implement `FromReflect` on +/// primitives and other Rust types internally. +/// +/// # Examples +/// +/// ```ignore +/// impl_from_reflect_value!(foo where T1: Bar); +/// ``` +/// +/// [deriving `FromReflect`]: FromReflect +/// [derives `Reflect`]: Reflect #[proc_macro] pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { let def = parse_macro_input!(input as ReflectValueDef); From 60fa2c772ccece951e50441bebaf74f935766c2b Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 31 Jan 2023 22:39:11 -0700 Subject: [PATCH 11/15] Document features --- crates/bevy_reflect/src/lib.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 591a01a577f56..5e4e95f542aec 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -350,6 +350,32 @@ //! assert_eq!(original_value, converted_value); //! ``` //! +//! # Features +//! +//! ## `bevy` +//! +//! | Default | Dependencies | +//! | :-----: | :---------------------------------------: | +//! | ❌ | [`bevy_math`], [`glam`], [`smallvec`] | +//! +//! This feature makes it so that the appropriate reflection traits are implemented on all the types +//! necessary for the [Bevy] game engine. +//! enables the optional dependencies: [`bevy_math`], [`glam`], and [`smallvec`]. +//! These dependencies are used by the [Bevy] game engine and must define their reflection implementations +//! within this crate due to Rust's [orphan rule]. +//! +//! ## `documentation` +//! +//! | Default | Dependencies | +//! | :-----: | :-------------------------------------------: | +//! | ❌ | [`bevy_reflect_derive/documentation`] | +//! +//! This feature enables capturing doc comments as strings for items that [derive `Reflect`]. +//! Documentation information can then be accessed at runtime on the [`TypeInfo`] of that item. +//! +//! This can be useful for generating documentation for scripting language interop or +//! for displaying tooltips in an editor. +//! //! [Reflection]: https://en.wikipedia.org/wiki/Reflective_programming //! [Bevy]: https://bevyengine.org/ //! [`bevy_reflect`]: crate @@ -368,6 +394,12 @@ //! [registry]: TypeRegistry //! [type information]: TypeInfo //! [type name]: Reflect::type_name +//! [`bevy_math`]: https://docs.rs/bevy_math/latest/bevy_math/ +//! [`glam`]: https://docs.rs/glam/latest/glam/ +//! [`smallvec`]: https://docs.rs/smallvec/latest/smallvec/ +//! [orphan rule]: https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type:~:text=But%20we%20can%E2%80%99t,implementation%20to%20use. +//! [`bevy_reflect_derive/documentation`]: bevy_reflect_derive +//! [derive `Reflect`]: bevy_reflect_derive::Reflect mod array; mod fields; From 51f318af6640b1748bcf7b3a87ca95e9a4694954 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 31 Jan 2023 23:38:38 -0700 Subject: [PATCH 12/15] Document type registration items --- crates/bevy_reflect/src/type_registry.rs | 55 ++++++++++++++++++------ 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 57bb841620047..c6c72142a6f30 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -40,9 +40,16 @@ impl Debug for TypeRegistryArc { } } -/// A trait which allows a type to generate its [`TypeRegistration`]. +/// A trait which allows a type to generate its [`TypeRegistration`] +/// for registration into the [`TypeRegistry`]. /// -/// This trait is automatically implemented for types which derive [`Reflect`]. +/// This trait is automatically implemented for items using the [`Reflect` derive macro]. +/// The macro also allows [`TypeData`] to be more easily registered. +/// +/// See the [crate-level documentation] for more information on type registration. +/// +/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect +/// [crate-level documentation]: crate pub trait GetTypeRegistration { fn get_type_registration() -> TypeRegistration; } @@ -269,19 +276,33 @@ impl TypeRegistryArc { } } -/// A record of data about a type. +/// Runtime storage for type metadata, registered into the [`TypeRegistry`]. +/// +/// An instance of `TypeRegistration` can be created using the [`TypeRegistration::of`] method, +/// but is more often automatically generated using the [`Reflect` derive macro] which itself generates +/// an implementation of the [`GetTypeRegistration`] trait. /// -/// This contains the [`TypeInfo`] of the type, as well as its [short name]. +/// Along with the type's [`TypeInfo`] and [short name], +/// this struct also contains a type's registered [`TypeData`]. +/// +/// See the [crate-level documentation] for more information on type registration. +/// +/// # Example +/// +/// ``` +/// # use bevy_reflect::{TypeRegistration, std_traits::ReflectDefault, FromType}; +/// let mut registration = TypeRegistration::of::>(); /// -/// For each trait specified by the [`#[reflect(_)]`][0] attribute of -/// [`#[derive(Reflect)]`][1] on the registered type, this record also contains -/// a [`TypeData`] which can be used to downcast [`Reflect`] trait objects of -/// this type to trait objects of the relevant trait. +/// assert_eq!("core::option::Option", registration.type_name()); +/// assert_eq!("Option", registration.short_name()); /// +/// registration.insert::(FromType::>::from_type()); +/// assert!(registration.data::().is_some()) +/// ``` +/// +/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect /// [short name]: bevy_utils::get_short_name -/// [`TypeInfo`]: crate::TypeInfo -/// [0]: crate::Reflect -/// [1]: crate::Reflect +/// [crate-level documentation]: crate pub struct TypeRegistration { short_name: String, data: HashMap>, @@ -377,9 +398,19 @@ impl Clone for TypeRegistration { } } } -/// A trait for types generated by the [`#[reflect_trait]`][0] attribute macro. + +/// A trait used to type-erase type metadata. +/// +/// Type data can be registered to the [`TypeRegistry`] and stored on a type's [`TypeRegistration`]. +/// +/// While type data is often generated using the [`#[reflect_trait]`][0] macro, +/// almost any type that implements [`Clone`] can be considered "type data". +/// This is because it has a blanket implementation over all `T` where `T: Clone + Send + Sync + 'static`. +/// +/// See the [crate-level documentation] for more information on type data and type registration. /// /// [0]: crate::reflect_trait +/// [crate-level documentation]: crate pub trait TypeData: Downcast + Send + Sync { fn clone_type_data(&self) -> Box; } From 00b1a21c6dfff619573187b635b2094f94c80e4e Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 31 Jan 2023 23:59:17 -0700 Subject: [PATCH 13/15] Update macro doc link syntax --- crates/bevy_reflect/bevy_reflect_derive/src/lib.rs | 11 ++++------- crates/bevy_reflect/src/enums/enum_trait.rs | 5 ++--- crates/bevy_reflect/src/lib.rs | 10 +++++----- crates/bevy_reflect/src/map.rs | 3 +-- crates/bevy_reflect/src/struct_trait.rs | 4 ++-- crates/bevy_reflect/src/tuple_struct.rs | 3 +-- crates/bevy_reflect/src/type_info.rs | 3 +-- crates/bevy_reflect/src/type_registry.rs | 12 ++++-------- 8 files changed, 20 insertions(+), 31 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index b17808db8538d..ece2015048d49 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -61,8 +61,8 @@ pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; /// one for `ReflectFoo` and another for `ReflectBar`. /// This assumes these types are indeed in-scope wherever this macro is called. /// -/// This is often used with traits that have been marked by the [`reflect_trait`] macro -/// in order to register the type's implementation of that trait. +/// This is often used with traits that have been marked by the [`#[reflect_trait]`](macro@reflect_trait) +/// macro in order to register the type's implementation of that trait. /// /// ### Special Identifiers /// @@ -148,7 +148,7 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { /// /// ## `#[reflect(ignore)]` /// -/// The `#[reflect(ignore)]` attribute is shared with the [`Reflect` derive macro] and has much of the same +/// The `#[reflect(ignore)]` attribute is shared with the [`#[derive(Reflect)]`](Reflect) macro and has much of the same /// functionality in that it marks a field to be ignored by the reflection API. /// /// The only major difference is that using it with this derive requires that the field implements [`Default`]. @@ -168,8 +168,6 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { /// or to remove the `Default` requirement on fields marked with `#[reflect(ignore)]`. /// Additionally, either form of this attribute can be used to fill in fields that are simply missing, /// such as when converting a partially-constructed dynamic type to a concrete one. -/// -/// [`Reflect` derive macro]: Reflect #[proc_macro_derive(FromReflect, attributes(reflect))] pub fn derive_from_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); @@ -204,7 +202,7 @@ pub fn derive_type_uuid(input: TokenStream) -> TokenStream { /// For a trait named `MyTrait`, this will generate the struct `ReflectMyTrait`. /// The generated struct can be created using `FromType` with any type that implements the trait. /// The creation and registration of this generated struct as type data can be automatically handled -/// by the [`Reflect` derive macro]. +/// by [`#[derive(Reflect)]`](Reflect). /// /// # Example /// @@ -243,7 +241,6 @@ pub fn derive_type_uuid(input: TokenStream) -> TokenStream { /// ``` /// /// [object-safe]: https://doc.rust-lang.org/reference/items/traits.html#object-safety -/// [`Reflect` derive macro]: Reflect #[proc_macro_attribute] pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { trait_reflection::reflect_trait(&args, input) diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 620575f5d415a..a4b9bec2a4512 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -39,7 +39,7 @@ use std::slice::Iter; /// /// # Implementation /// -/// > 💡 This trait can be automatically implemented using the [`Reflect` derive macro] +/// > 💡 This trait can be automatically implemented using [`#[derive(Reflect)]`](derive@crate::Reflect) /// > on an enum definition. /// /// Despite the fact that enums can represent multiple states, traits only exist in one state @@ -52,7 +52,7 @@ use std::slice::Iter; /// accessing fields! /// Again, this is to account for _all three_ variant types. /// -/// We recommend using the built-in [`Reflect` derive macro] to automatically handle all the +/// We recommend using the built-in [`#[derive(Reflect)]`](derive@crate::Reflect) macro to automatically handle all the /// implementation details for you. /// However, if you _must_ implement this trait manually, there are a few things to keep in mind... /// @@ -87,7 +87,6 @@ use std::slice::Iter; /// [`None`]: core::option::Option::None /// [`Some`]: core::option::Option::Some /// [`Reflect`]: bevy_reflect_derive::Reflect -/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect pub trait Enum: Reflect { /// Returns a reference to the value of the field (in the current variant) with the given name. /// diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 5e4e95f542aec..95daffcd4a1f2 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -217,7 +217,7 @@ //! //! With the derive macro, fields can be ignored or given default values for when a field is missing //! in the passed value. -//! See the [derive macro documentation](bevy_reflect_derive::FromReflect) for details. +//! See the [derive macro documentation](derive@crate::FromReflect) for details. //! //! All primitives and simple types implement `FromReflect` by relying on their [`Default`] implementation. //! @@ -270,7 +270,7 @@ //! These allow traits to be used directly on a `dyn Reflect` while utilizing the underlying type's implementation. //! //! For any [object-safe] trait, we can easily generate a corresponding `ReflectTrait` type for our trait -//! using the [`reflect_trait`] macro. +//! using the [`#[reflect_trait]`](reflect_trait) macro. //! //! ``` //! # use bevy_reflect::{Reflect, reflect_trait, TypeRegistry}; @@ -380,9 +380,9 @@ //! [Bevy]: https://bevyengine.org/ //! [`bevy_reflect`]: crate //! [runtime cost]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch -//! [derive macro]: bevy_reflect_derive::Reflect +//! [derive macro]: derive@crate::Reflect //! [`'static` lifetime]: https://doc.rust-lang.org/rust-by-example/scope/lifetime/static_lifetime.html#trait-bound -//! [derive macro documentation]: bevy_reflect_derive::Reflect +//! [derive macro documentation]: derive@crate::Reflect //! [type data]: TypeData //! [`ReflectDefault`]: std_traits::ReflectDefault //! [object-safe]: https://doc.rust-lang.org/reference/items/traits.html#object-safety @@ -399,7 +399,7 @@ //! [`smallvec`]: https://docs.rs/smallvec/latest/smallvec/ //! [orphan rule]: https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type:~:text=But%20we%20can%E2%80%99t,implementation%20to%20use. //! [`bevy_reflect_derive/documentation`]: bevy_reflect_derive -//! [derive `Reflect`]: bevy_reflect_derive::Reflect +//! [derive `Reflect`]: derive@crate::Reflect mod array; mod fields; diff --git a/crates/bevy_reflect/src/map.rs b/crates/bevy_reflect/src/map.rs index 70082330e9df4..8dcd28386a2e5 100644 --- a/crates/bevy_reflect/src/map.rs +++ b/crates/bevy_reflect/src/map.rs @@ -16,7 +16,7 @@ use crate::{DynamicInfo, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo /// # Hashing /// /// All keys are expected to return a valid hash value from [`Reflect::reflect_hash`]. -/// If using the [`Reflect` derive macro], this can be done by adding `#[reflect(Hash)]` +/// If using the [`#[derive(Reflect)]`](derive@crate::Reflect) macro, this can be done by adding `#[reflect(Hash)]` /// to the entire struct or enum. /// This is true even for manual implementors who do not use the hashed value, /// as it is still relied on by [`DynamicMap`]. @@ -39,7 +39,6 @@ use crate::{DynamicInfo, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo /// [map-like]: https://doc.rust-lang.org/book/ch08-03-hash-maps.html /// [reflection]: crate /// [`HashMap`]: bevy_utils::HashMap -/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect pub trait Map: Reflect { /// Returns a reference to the value associated with the given key. /// diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index 0245af9b09694..ab146d1b36a43 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -15,7 +15,7 @@ use std::{ /// This trait uses the [`Reflect`] trait to allow implementors to have their fields /// be dynamically addressed by both name and index. /// -/// When using the [`Reflect` derive macro] on a standard struct, +/// When using [`#[derive(Reflect)]`](derive@crate::Reflect) on a standard struct, /// this trait will be automatically implemented. /// This goes for [unit structs] as well. /// @@ -40,7 +40,7 @@ use std::{ /// /// [struct-like]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html /// [reflection]: crate -/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect + /// [unit structs]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html#unit-like-structs-without-any-fields pub trait Struct: Reflect { /// Returns a reference to the value of the field named `name` as a `&dyn diff --git a/crates/bevy_reflect/src/tuple_struct.rs b/crates/bevy_reflect/src/tuple_struct.rs index 22c5e200486f6..33edd7bb172e3 100644 --- a/crates/bevy_reflect/src/tuple_struct.rs +++ b/crates/bevy_reflect/src/tuple_struct.rs @@ -11,7 +11,7 @@ use std::slice::Iter; /// This trait uses the [`Reflect`] trait to allow implementors to have their fields /// be dynamically addressed by index. /// -/// When using the [`Reflect` derive macro] on a tuple struct, +/// When using [`#[derive(Reflect)]`](derive@crate::Reflect) on a tuple struct, /// this trait will be automatically implemented. /// /// # Example @@ -32,7 +32,6 @@ use std::slice::Iter; /// /// [tuple struct-like]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html#using-tuple-structs-without-named-fields-to-create-different-types /// [reflection]: crate -/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect pub trait TupleStruct: Reflect { /// Returns a reference to the value of the field with index `index` as a /// `&dyn Reflect`. diff --git a/crates/bevy_reflect/src/type_info.rs b/crates/bevy_reflect/src/type_info.rs index 3e8336be9bfe9..27df34ea767e3 100644 --- a/crates/bevy_reflect/src/type_info.rs +++ b/crates/bevy_reflect/src/type_info.rs @@ -5,7 +5,7 @@ use std::any::{Any, TypeId}; /// A static accessor to compile-time type information. /// -/// This trait is automatically implemented by the [`Reflect` derive macro] +/// This trait is automatically implemented by the [`#[derive(Reflect)]`](derive@crate::Reflect) macro /// and allows type information to be processed without an instance of that type. /// /// # Implementing @@ -65,7 +65,6 @@ use std::any::{Any, TypeId}; /// # } /// ``` /// -/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect /// [utility]: crate::utility pub trait Typed: Reflect { /// Returns the compile-time [info] for the underlying type. diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index c6c72142a6f30..c19ce9bd831af 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -11,13 +11,12 @@ use std::{any::TypeId, fmt::Debug, sync::Arc}; /// This struct is used as the central store for type information. /// [Registering] a type will generate a new [`TypeRegistration`] entry in this store /// using a type's [`GetTypeRegistration`] implementation -/// (which will is automatically implemented when using the [`Reflect` derive macro]). +/// (which is automatically implemented when using [`#[derive(Reflect)]`](derive@crate::Reflect)). /// /// See the [crate-level documentation] for more information. /// /// [reflected]: crate /// [Registering]: TypeRegistry::register -/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect /// [crate-level documentation]: crate pub struct TypeRegistry { registrations: HashMap, @@ -43,12 +42,11 @@ impl Debug for TypeRegistryArc { /// A trait which allows a type to generate its [`TypeRegistration`] /// for registration into the [`TypeRegistry`]. /// -/// This trait is automatically implemented for items using the [`Reflect` derive macro]. +/// This trait is automatically implemented for items using [`#[derive(Reflect)]`](derive@crate::Reflect). /// The macro also allows [`TypeData`] to be more easily registered. /// /// See the [crate-level documentation] for more information on type registration. /// -/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect /// [crate-level documentation]: crate pub trait GetTypeRegistration { fn get_type_registration() -> TypeRegistration; @@ -279,7 +277,7 @@ impl TypeRegistryArc { /// Runtime storage for type metadata, registered into the [`TypeRegistry`]. /// /// An instance of `TypeRegistration` can be created using the [`TypeRegistration::of`] method, -/// but is more often automatically generated using the [`Reflect` derive macro] which itself generates +/// but is more often automatically generated using [`#[derive(Reflect)]`](derive@crate::Reflect) which itself generates /// an implementation of the [`GetTypeRegistration`] trait. /// /// Along with the type's [`TypeInfo`] and [short name], @@ -300,7 +298,6 @@ impl TypeRegistryArc { /// assert!(registration.data::().is_some()) /// ``` /// -/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect /// [short name]: bevy_utils::get_short_name /// [crate-level documentation]: crate pub struct TypeRegistration { @@ -403,13 +400,12 @@ impl Clone for TypeRegistration { /// /// Type data can be registered to the [`TypeRegistry`] and stored on a type's [`TypeRegistration`]. /// -/// While type data is often generated using the [`#[reflect_trait]`][0] macro, +/// While type data is often generated using the [`#[reflect_trait]`](crate::reflect_trait) macro, /// almost any type that implements [`Clone`] can be considered "type data". /// This is because it has a blanket implementation over all `T` where `T: Clone + Send + Sync + 'static`. /// /// See the [crate-level documentation] for more information on type data and type registration. /// -/// [0]: crate::reflect_trait /// [crate-level documentation]: crate pub trait TypeData: Downcast + Send + Sync { fn clone_type_data(&self) -> Box; From 690797b6b6d5cf193107940a96144e5a9786d3ae Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 18 Feb 2023 11:28:05 -0700 Subject: [PATCH 14/15] Address PR comments --- crates/bevy_reflect/bevy_reflect_derive/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index ece2015048d49..086441a311270 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -149,7 +149,7 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { /// ## `#[reflect(ignore)]` /// /// The `#[reflect(ignore)]` attribute is shared with the [`#[derive(Reflect)]`](Reflect) macro and has much of the same -/// functionality in that it marks a field to be ignored by the reflection API. +/// functionality in that it denotes that a field will be ignored by the reflection API. /// /// The only major difference is that using it with this derive requires that the field implements [`Default`]. /// Without this requirement, there would be no way for `FromReflect` to automatically construct missing fields From f7b3918172e3f6f99d8e99e1645bbff4d9919f87 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 18 Feb 2023 13:10:37 -0700 Subject: [PATCH 15/15] Add Limitations section to docs --- crates/bevy_reflect/src/lib.rs | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 95daffcd4a1f2..c7d87436707fc 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -15,6 +15,9 @@ //! * Access type metadata at runtime //! * Serialize and deserialize (i.e. save and load) data //! +//! It's important to note that because of missing features in Rust, +//! there are some [limitations] with this crate. +//! //! # The `Reflect` Trait //! //! At the core of [`bevy_reflect`] is the [`Reflect`] trait. @@ -350,6 +353,36 @@ //! assert_eq!(original_value, converted_value); //! ``` //! +//! # Limitations +//! +//! While this crate offers a lot in terms of adding reflection to Rust, +//! it does come with some limitations that don't make it as featureful as reflection +//! in other programming languages. +//! +//! ## Non-Static Lifetimes +//! +//! One of the most obvious limitations is the `'static` requirement. +//! Rust requires fields to define a lifetime for referenced data, +//! but [`Reflect`] requires all types to have a `'static` lifetime. +//! This makes it impossible to reflect any type with non-static borrowed data. +//! +//! ## Function Reflection +//! +//! Another limitation is the inability to fully reflect functions and methods. +//! Most languages offer some way of calling methods dynamically, +//! but Rust makes this very difficult to do. +//! For non-generic methods, this can be done by registering custom [type data] that +//! contains function pointers. +//! For generic methods, the same can be done but will typically require manual monomorphization +//! (i.e. manually specifying the types the generic method can take). +//! +//! ## Manual Registration +//! +//! Since Rust doesn't provide built-in support for running initialization code before `main`, +//! there is no way for `bevy_reflect` to automatically register types into the [type registry]. +//! This means types must manually be registered, including their desired monomorphized +//! representations if generic. +//! //! # Features //! //! ## `bevy` @@ -378,6 +411,7 @@ //! //! [Reflection]: https://en.wikipedia.org/wiki/Reflective_programming //! [Bevy]: https://bevyengine.org/ +//! [limitations]: #limitations //! [`bevy_reflect`]: crate //! [runtime cost]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch //! [derive macro]: derive@crate::Reflect @@ -394,6 +428,7 @@ //! [registry]: TypeRegistry //! [type information]: TypeInfo //! [type name]: Reflect::type_name +//! [type registry]: TypeRegistry //! [`bevy_math`]: https://docs.rs/bevy_math/latest/bevy_math/ //! [`glam`]: https://docs.rs/glam/latest/glam/ //! [`smallvec`]: https://docs.rs/smallvec/latest/smallvec/