implement MapEntities for higher-order types (#19071)

# Objective

With the current `MapEntities` `impl`s, it is not possible to derive
things like this:

```rust
#[derive(Component)]
pub struct Inventory {
  #[entities]
  slots: Vec<Option<Entity>>,
}
```

This is because `MapEntities` is only implemented for `Vec<Entity>` &
`Option<Entity>`, and not arbitrary combinations of those.

It would be nice to also support those types.

## Solution

I replaced the `impl`s of the following types

- `Option<Entity>`: replaced with `Option<T>` 
- `Vec<Entity>`: replaced with `Vec<T>`
- `HashSet<Entity, S>`: replaced with `HashSet<T, S>`
- `T` also had to be `Eq + core:#️⃣:Hash` here. **Not sure if this is
too restrictive?**
- `IndexSet<Entity, S>`: replaced with `IndexSet <T, S>`
- `T` also had to be `Eq + core:#️⃣:Hash` here. **Not sure if this is
too restrictive?**
- `BTreeSet<Entity>`: replaced with `BTreeSet<T>`
- `VecDeque<Entity>`: replaced with `VecDeque<T>`
- `SmallVec<A: smallvec::Array<Item = Entity>>`: replaced with
`SmallVec<A: smallvec::Array<Item = T>>`

(in all of the above, `T` is a generic type that implements
`MapEntities` (`Entity` being one of them).)

## Testing

I did not test any of this, but extended the `Component::map_entities`
doctest with an example usage of the newly supported types.

---

## Showcase

With these changes, this is now possible:

```rust
#[derive(Component)]
pub struct Inventory {
  #[entities]
  slots: Vec<Option<Entity>>,
}
```
This commit is contained in:
Han Kruiger 2025-05-06 07:24:37 +02:00 committed by GitHub
parent 8f3e45b45f
commit b8724c21ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 45 additions and 18 deletions

View File

@ -574,6 +574,17 @@ pub trait Component: Send + Sync + 'static {
/// ``` /// ```
/// ///
/// Fields with `#[entities]` must implement [`MapEntities`](crate::entity::MapEntities). /// Fields with `#[entities]` must implement [`MapEntities`](crate::entity::MapEntities).
///
/// Bevy provides various implementations of [`MapEntities`](crate::entity::MapEntities), so that arbitrary combinations like these are supported with `#[entities]`:
///
/// ```rust
/// # use bevy_ecs::{component::Component, entity::Entity};
/// #[derive(Component)]
/// struct Inventory {
/// #[entities]
/// items: Vec<Option<Entity>>
/// }
/// ```
#[inline] #[inline]
fn map_entities<E: EntityMapper>(_this: &mut Self, _mapper: &mut E) {} fn map_entities<E: EntityMapper>(_this: &mut Self, _mapper: &mut E) {}
} }

View File

@ -65,25 +65,38 @@ impl MapEntities for Entity {
} }
} }
impl MapEntities for Option<Entity> { impl<T: MapEntities> MapEntities for Option<T> {
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) { fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
if let Some(entity) = self { if let Some(entities) = self {
*entity = entity_mapper.get_mapped(*entity); entities.map_entities(entity_mapper);
} }
} }
} }
impl<S: BuildHasher + Default> MapEntities for HashSet<Entity, S> { impl<T: MapEntities + Eq + core::hash::Hash, S: BuildHasher + Default> MapEntities
for HashSet<T, S>
{
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) { fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
*self = self.drain().map(|e| entity_mapper.get_mapped(e)).collect(); *self = self
.drain()
.map(|mut entities| {
entities.map_entities(entity_mapper);
entities
})
.collect();
} }
} }
impl<S: BuildHasher + Default> MapEntities for IndexSet<Entity, S> { impl<T: MapEntities + Eq + core::hash::Hash, S: BuildHasher + Default> MapEntities
for IndexSet<T, S>
{
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) { fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
*self = self *self = self
.drain(..) .drain(..)
.map(|e| entity_mapper.get_mapped(e)) .map(|mut entities| {
entities.map_entities(entity_mapper);
entities
})
.collect(); .collect();
} }
} }
@ -97,35 +110,38 @@ impl MapEntities for EntityIndexSet {
} }
} }
impl MapEntities for BTreeSet<Entity> { impl<T: MapEntities + Ord> MapEntities for BTreeSet<T> {
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) { fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
*self = mem::take(self) *self = mem::take(self)
.into_iter() .into_iter()
.map(|e| entity_mapper.get_mapped(e)) .map(|mut entities| {
entities.map_entities(entity_mapper);
entities
})
.collect(); .collect();
} }
} }
impl MapEntities for Vec<Entity> { impl<T: MapEntities> MapEntities for Vec<T> {
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) { fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
for entity in self.iter_mut() { for entities in self.iter_mut() {
*entity = entity_mapper.get_mapped(*entity); entities.map_entities(entity_mapper);
} }
} }
} }
impl MapEntities for VecDeque<Entity> { impl<T: MapEntities> MapEntities for VecDeque<T> {
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) { fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
for entity in self.iter_mut() { for entities in self.iter_mut() {
*entity = entity_mapper.get_mapped(*entity); entities.map_entities(entity_mapper);
} }
} }
} }
impl<A: smallvec::Array<Item = Entity>> MapEntities for SmallVec<A> { impl<T: MapEntities, A: smallvec::Array<Item = T>> MapEntities for SmallVec<A> {
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) { fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
for entity in self.iter_mut() { for entities in self.iter_mut() {
*entity = entity_mapper.get_mapped(*entity); entities.map_entities(entity_mapper);
} }
} }
} }