Derive Macro bevy_reflect_derive::Reflect

source ·
#[derive(Reflect)]
{
    // Attributes available to this derive:
    #[reflect]
    #[reflect_value]
    #[type_path]
    #[type_name]
}
Expand description

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 implementations for Reflect, Typed, GetTypeRegistration, and FromReflect. And, depending on the item’s structure, will either implement Struct, TupleStruct, or Enum.

See the FromReflect derive macro for more information on how to customize the FromReflect implementation.

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.

In addition to those listed, this macro can also use the attributes for TypePath derives.

#[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.

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.

Default Registrations

The following types are automatically registered when deriving Reflect:

  • ReflectFromReflect (unless opting out of FromReflect)
  • SerializationData
  • ReflectFromPtr

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 (for structs and tuple structs only).

#[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.

#[reflect(from_reflect = false)]

This attribute will opt-out of the default FromReflect implementation.

This is useful for when a type can’t or shouldn’t implement FromReflect, or if a manual implementation is desired.

Note that in the latter case, ReflectFromReflect will no longer be automatically registered.

#[reflect(type_path = false)]

This attribute will opt-out of the default TypePath implementation.

This is useful for when a type can’t or shouldn’t implement TypePath, or if a manual implementation is desired.

#[reflect(no_field_bounds)]

This attribute will opt-out of the default trait bounds added to all field types for the generated reflection trait impls.

Normally, all fields will have the bounds TypePath, and either FromReflect or Reflect depending on if #[reflect(from_reflect = false)] is used. However, this might not always be desirable, and so this attribute may be used to remove those bounds.

Example

If a type is recursive the default bounds will cause an overflow error when building:

#[derive(Reflect)] // ERROR: overflow evaluating the requirement `Foo: FromReflect`
struct Foo {
  foo: Vec<Foo>,
}

// Generates a where clause like:
// impl bevy_reflect::Reflect for Foo
// where
//   Self: Any + Send + Sync,
//   Vec<Foo>: FromReflect + TypePath,

In this case, Foo is given the bounds Vec<Foo>: FromReflect + TypePath, which requires that Foo implements FromReflect, which requires that Vec<Foo> implements FromReflect, and so on, resulting in the error.

To fix this, we can add #[reflect(no_field_bounds)] to Foo to remove the bounds on Vec<Foo>:

#[derive(Reflect)]
#[reflect(no_field_bounds)]
struct Foo {
  foo: Vec<Foo>,
}

// Generates a where clause like:
// impl bevy_reflect::Reflect for Foo
// where
//   Self: Any + Send + Sync,

#[reflect(where T: Trait, U::Assoc: Trait, ...)]

This attribute can be used to add additional bounds to the generated reflection trait impls.

This is useful for when a type needs certain bounds only applied to the reflection impls that are not otherwise automatically added by the derive macro.

Example

In the example below, we want to enforce that T::Assoc: List is required in order for Foo<T> to be reflectable, but we don’t want it to prevent Foo<T> from being used in places where T::Assoc: List is not required.

trait Trait {
  type Assoc;
}

#[derive(Reflect)]
#[reflect(where T::Assoc: List)]
struct Foo<T: Trait> where T::Assoc: Default {
  value: T::Assoc,
}

// Generates a where clause like:
//
// impl<T: Trait> bevy_reflect::Reflect for Foo<T>
// where
//   Self: Any + Send + Sync,
//   T::Assoc: Default,
//   T: TypePath,
//   T::Assoc: FromReflect + TypePath,
//   T::Assoc: List,
// {/* ... */}

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 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.

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.