bevy/crates/bevy_reflect/derive/src
Gino Valente aa241672e1
bevy_reflect: Nested TypeInfo getters (#13321)
# Objective

Right now, `TypeInfo` can be accessed directly from a type using either
`Typed::type_info` or `Reflect::get_represented_type_info`.

However, once that `TypeInfo` is accessed, any nested types must be
accessed via the `TypeRegistry`.

```rust
#[derive(Reflect)]
struct Foo {
  bar: usize
}

let registry = TypeRegistry::default();

let TypeInfo::Struct(type_info) = Foo::type_info() else {
  panic!("expected struct info");
};

let field = type_info.field("bar").unwrap();

let field_info = registry.get_type_info(field.type_id()).unwrap();
assert!(field_info.is::<usize>());;
```

## Solution

Enable nested types within a `TypeInfo` to be retrieved directly.

```rust
#[derive(Reflect)]
struct Foo {
  bar: usize
}

let TypeInfo::Struct(type_info) = Foo::type_info() else {
  panic!("expected struct info");
};

let field = type_info.field("bar").unwrap();

let field_info = field.type_info().unwrap();
assert!(field_info.is::<usize>());;
```

The particular implementation was chosen for two reasons.

Firstly, we can't just store `TypeInfo` inside another `TypeInfo`
directly. This is because some types are recursive and would result in a
deadlock when trying to create the `TypeInfo` (i.e. it has to create the
`TypeInfo` before it can use it, but it also needs the `TypeInfo` before
it can create it). Therefore, we must instead store the function so it
can be retrieved lazily.

I had considered also using a `OnceLock` or something to lazily cache
the info, but I figured we can look into optimizations later. The API
should remain the same with or without the `OnceLock`.

Secondly, a new wrapper trait had to be introduced: `MaybeTyped`. Like
`RegisterForReflection`, this trait is `#[doc(hidden)]` and only exists
so that we can properly handle dynamic type fields without requiring
them to implement `Typed`. We don't want dynamic types to implement
`Typed` due to the fact that it would make the return type
`Option<&'static TypeInfo>` for all types even though only the dynamic
types ever need to return `None` (see #6971 for details).

Users should never have to interact with this trait as it has a blanket
impl for all `Typed` types. And `Typed` is automatically implemented
when deriving `Reflect` (as it is required).

The one downside is we do need to return `Option<&'static TypeInfo>`
from all these new methods so that we can handle the dynamic cases. If
we didn't have to, we'd be able to get rid of the `Option` entirely. But
I think that's an okay tradeoff for this one part of the API, and keeps
the other APIs intact.

## Testing

This PR contains tests to verify everything works as expected. You can
test locally by running:

```
cargo test --package bevy_reflect
```

---

## Changelog

### Public Changes

- Added `ArrayInfo::item_info` method
- Added `NamedField::type_info` method
- Added `UnnamedField::type_info` method
- Added `ListInfo::item_info` method
- Added `MapInfo::key_info` method
- Added `MapInfo::value_info` method
- All active fields now have a `Typed` bound (remember that this is
automatically satisfied for all types that derive `Reflect`)

### Internal Changes

- Added `MaybeTyped` trait

## Migration Guide

All active fields for reflected types (including lists, maps, tuples,
etc.), must implement `Typed`. For the majority of users this won't have
any visible impact.

However, users implementing `Reflect` manually may need to update their
types to implement `Typed` if they weren't already.

Additionally, custom dynamic types will need to implement the new hidden
`MaybeTyped` trait.
2024-07-15 00:40:07 +00:00
..
impls bevy_reflect: Feature-gate function reflection (#14174) 2024-07-14 15:55:31 +00:00
container_attributes.rs Apply Clippy lints regarding lazy evaluation and closures (#14015) 2024-07-01 15:54:40 +00:00
custom_attributes.rs bevy_reflect: Custom attributes (#11659) 2024-05-20 19:30:21 +00:00
derive_data.rs Apply Clippy lints regarding lazy evaluation and closures (#14015) 2024-07-01 15:54:40 +00:00
documentation.rs Rename bevy_reflect_derive folder to derive (#13269) 2024-05-07 07:55:32 +00:00
enum_utility.rs bevy_reflect: enum_utility cleanup (#13424) 2024-05-22 21:18:57 +00:00
field_attributes.rs bevy_reflect: Custom attributes (#11659) 2024-05-20 19:30:21 +00:00
from_reflect.rs bevy_reflect: enum_utility cleanup (#13424) 2024-05-22 21:18:57 +00:00
lib.rs bevy_reflect: Custom attributes (#11659) 2024-05-20 19:30:21 +00:00
reflect_value.rs bevy_reflect: Remove ContainerAttributes::merge (#13303) 2024-05-09 18:17:54 +00:00
registration.rs Rename bevy_reflect_derive folder to derive (#13269) 2024-05-07 07:55:32 +00:00
serialization.rs Rename bevy_reflect_derive folder to derive (#13269) 2024-05-07 07:55:32 +00:00
trait_reflection.rs Rename bevy_reflect_derive folder to derive (#13269) 2024-05-07 07:55:32 +00:00
type_path.rs Rename bevy_reflect_derive folder to derive (#13269) 2024-05-07 07:55:32 +00:00
utility.rs bevy_reflect: Nested TypeInfo getters (#13321) 2024-07-15 00:40:07 +00:00