Commit Graph

4 Commits

Author SHA1 Message Date
Gino Valente
3892adcb47
bevy_reflect: Add Type type (#14838)
# Objective

Closes #7622.

I was working on adding support for reflecting generic functions and
found that I wanted to use an argument's `TypeId` for hashing and
comparison, but its `TypePath` for debugging and error messaging.

While I could just keep them separate, place them in a tuple or a local
struct or something, I think I see an opportunity to make a dedicate
type for this.

Additionally, we can use this type to clean up some duplication amongst
the type info structs in a manner similar to #7622.

## Solution

Added the `Type` type. This should be seen as the most basic
representation of a type apart from `TypeId`. It stores both the
`TypeId` of the type as well as its `TypePathTable`.

The `Hash` and `PartialEq` implementations rely on the `TypeId`, while
the `Debug` implementation relies on the `TypePath`.

This makes it especially useful as a key in a `HashMap` since we get the
speed of the `TypeId` hashing/comparisons with the readability of
`TypePath`.

With this type, we're able to reduce the duplication across the type
info structs by removing individual fields for `TypeId` and
`TypePathTable`, replacing them with a single `Type` field. Similarly,
we can remove many duplicate methods and replace it with a macro that
delegates to the stored `Type`.

### Caveats

It should be noted that this type is currently 3x larger than `TypeId`.
On my machine, it's 48 bytes compared to `TypeId`'s 16. While this
doesn't matter for `TypeInfo` since it would contain that data
regardless, it is something to keep in mind when using elsewhere.

## Testing

All tests should pass as normal:

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

---

## Showcase

`bevy_reflect` now exports a `Type` struct. This type contains both the
`TypeId` and the `TypePathTable` of the given type, allowing it to be
used like `TypeId` but have the debuggability of `TypePath`.

```rust
// We can create this for any type implementing `TypePath`:
let ty = Type::of::<String>();

// It has `Hash` and `Eq` impls powered by `TypeId`, making it useful for maps:
let mut map = HashMap::<Type, i32>::new();
map.insert(ty, 25);

// And it has a human-readable `Debug` representation:
let debug = format!("{:?}", map);
assert_eq!(debug, "{alloc::string::String: 25}");
```

## Migration Guide

Certain type info structs now only return their item types as `Type`
instead of exposing direct methods on them.

The following methods have been removed:

- `ArrayInfo::item_type_path_table`
- `ArrayInfo::item_type_id`
- `ArrayInfo::item_is`
- `ListInfo::item_type_path_table`
- `ListInfo::item_type_id`
- `ListInfo::item_is`
- `SetInfo::value_type_path_table`
- `SetInfo::value_type_id`
- `SetInfo::value_is`
- `MapInfo::key_type_path_table`
- `MapInfo::key_type_id`
- `MapInfo::key_is`
- `MapInfo::value_type_path_table`
- `MapInfo::value_type_id`
- `MapInfo::value_is`

Instead, access the `Type` directly using one of the new methods:

- `ArrayInfo::item_ty`
- `ListInfo::item_ty`
- `SetInfo::value_ty`
- `MapInfo::key_ty`
- `MapInfo::value_ty`

For example:

```rust
// BEFORE
let type_id = array_info.item_type_id();

// AFTER
let type_id = array_info.item_ty().id();
```
2024-08-25 17:57:07 +00:00
radiish
6ab8767d3b
reflect: implement the unique reflect rfc (#7207)
# Objective

- Implements the [Unique Reflect
RFC](https://github.com/nicopap/rfcs/blob/bevy-reflect-api/rfcs/56-better-reflect.md).

## Solution

- Implements the RFC.
- This implementation differs in some ways from the RFC:
- In the RFC, it was suggested `Reflect: Any` but `PartialReflect:
?Any`. During initial implementation I tried this, but we assume the
`PartialReflect: 'static` in a lot of places and the changes required
crept out of the scope of this PR.
- `PartialReflect::try_into_reflect` originally returned `Option<Box<dyn
Reflect>>` but i changed this to `Result<Box<dyn Reflect>, Box<dyn
PartialReflect>>` since the method takes by value and otherwise there
would be no way to recover the type. `as_full` and `as_full_mut` both
still return `Option<&(mut) dyn Reflect>`.

---

## Changelog

- Added `PartialReflect`.
- `Reflect` is now a subtrait of `PartialReflect`.
- Moved most methods on `Reflect` to the new `PartialReflect`.
- Added `PartialReflect::{as_partial_reflect, as_partial_reflect_mut,
into_partial_reflect}`.
- Added `PartialReflect::{try_as_reflect, try_as_reflect_mut,
try_into_reflect}`.
- Added `<dyn PartialReflect>::{try_downcast_ref, try_downcast_mut,
try_downcast, try_take}` supplementing the methods on `dyn Reflect`.

## Migration Guide

- Most instances of `dyn Reflect` should be changed to `dyn
PartialReflect` which is less restrictive, however trait bounds should
generally stay as `T: Reflect`.
- The new `PartialReflect::{as_partial_reflect, as_partial_reflect_mut,
into_partial_reflect, try_as_reflect, try_as_reflect_mut,
try_into_reflect}` methods as well as `Reflect::{as_reflect,
as_reflect_mut, into_reflect}` will need to be implemented for manual
implementors of `Reflect`.

## Future Work

- This PR is designed to be followed up by another "Unique Reflect Phase
2" that addresses the following points:
- Investigate making serialization revolve around `Reflect` instead of
`PartialReflect`.
- [Remove the `try_*` methods on `dyn PartialReflect` since they are
stop
gaps](https://github.com/bevyengine/bevy/pull/7207#discussion_r1083476050).
- Investigate usages like `ReflectComponent`. In the places they
currently use `PartialReflect`, should they be changed to use `Reflect`?
- Merging this opens the door to lots of reflection features we haven't
been able to implement.
- We could re-add [the `Reflectable`
trait](8e3488c880/crates/bevy_reflect/src/reflect.rs (L337-L342))
and make `FromReflect` a requirement to improve [`FromReflect`
ergonomics](https://github.com/bevyengine/rfcs/pull/59). This is
currently not possible because dynamic types cannot sensibly be
`FromReflect`.
  - Since this is an alternative to #5772, #5781 would be made cleaner.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
2024-08-12 17:01:41 +00:00
Gino Valente
297c0a3954
bevy_reflect: Add DynamicSet to dynamic_types example (#14665)
# Objective

The `dynamic_types` example was missing a reference to the newly added
`DynamicSet` type.

## Solution

Add `DynamicSet` to the `dynamic_types` example.

For parity with the other dynamic types, I also implemented
`FromIterator<T: Reflect>`, `FromIterator<Box<dyn Reflect>>`, and
`IntoIterator for &DynamicSet`.

## Testing

You can run the example locally:

```
cargo run --example dynamic_types
```
2024-08-08 22:26:18 +00:00
Robert Walter
52a2a3b146
Dedicated Reflect implementation for Set-like things (#13014)
# Objective

I just wanted to inspect `HashSet`s in `bevy-inspector-egui` but I
noticed that it didn't work for some reason. A few minutes later I found
myself looking into the bevy reflect impls noticing that `HashSet`s have
been covered only rudimentary up until now.

## Solution

I'm not sure if this is overkill (especially the first bullet), but
here's a list of the changes:

- created a whole new trait and enum variants for `ReflectRef` and the
like called `Set`
- mostly oriented myself at the `Map` trait and made the necessary
changes until RA was happy
- create macro `impl_reflect_for_hashset!` and call it on `std::HashSet`
and `hashbrown::HashSet`

Extra notes:

- no `get_mut` or `get_mut_at` mirroring the `std::HashSet`
- `insert[_boxed]` and `remove` return `bool` mirroring `std::HashSet`,
additionally that bool is reflect as I thought that would be how we
handle things in bevy reflect, but I'm not sure on this
- ser/de are handled via `SeqAccess`
- I'm not sure about the general deduplication property of this impl of
`Set` that is generally expected? I'm also not sure yet if `Map` does
provide this. This mainly refers to the `Dynamic[...]` structs
- I'm not sure if there are other methods missing from the `trait`, I
felt like `contains` or the set-operations (union/diff/...) could've
been helpful, but I wanted to get out the bare minimum for feedback
first

---

## Changelog

### Added
- `Set` trait for `bevy_reflect`

### Changed
- `std::collections::HashSet` and `bevy_utils::hashbrown::HashSet` now
implement a more complete set of reflect functionalities instead of
"just" `reflect_value`
- `TypeInfo` contains a new variant `Set` that contains `SetInfo`
- `ReflectKind` contains a new variant `Set`
- `ReflectRef` contains a new variant `Set`
- `ReflectMut` contains a new variant `Set`
- `ReflectOwned` contains a new variant `Set`

## Migration Guide

- The new `Set` variants on the enums listed in the change section
should probably be considered by people working with this level of the
lib
### Help wanted! 

I'm not sure if this change is able to break code. From my understanding
it shouldn't since we just add functionality but I'm not sure yet if
theres anything missing from my impl that would be normally provided by
`impl_reflect_value!`
2024-07-24 19:43:26 +00:00