Skip to content

Commit

Permalink
implement UniqueEntityVec (#17549)
Browse files Browse the repository at this point in the history
# Objective

In #16547, we added `EntitySet`s/`EntitySetIterator`s. We can know
whenever an iterator only contains unique entities, however we do not
yet have the ability to collect and reuse these without either the
unsafe `UniqueEntityIter::from_iterator_unchecked`, or the expensive
`HashSet::from_iter`.
An important piece for being able to do this is a `Vec` that maintains
the uniqueness property, can be collected into, and is itself
`EntitySet`.

A lot of entity collections are already intended to be "unique", but
have no way of expressing that when stored, other than using an
aforementioned `HashSet`. Such a type helps by limiting or even removing
the need for unsafe on the user side when not using a validated `Set`
type, and makes it easier to interface with other infrastructure like
f.e. `RelationshipSourceCollection`s.

## Solution

We implement `UniqueEntityVec`. 

This is a wrapper around `Vec`, that only ever contains unique elements.
It mirrors the API of `Vec`, however restricts any mutation as to not
violate the uniqueness guarantee. Meaning:
- Any inherent method which can introduce new elements or mutate
existing ones is now unsafe, f.e.: `insert`, `retain_mut`
- Methods that are impossible to use safely are omitted, f.e.: `fill`,
`extend_from_within`

A handful of the unsafe methods can do element-wise mutation
(`retain_mut`, `dedup_by`), which can be an unwind safety hazard were
the element-wise operation to panic. For those methods, we require that
each individual execution of the operation upholds uniqueness, not just
the entire method as a whole.

To be safe for mutable usage, slicing and the associated slice methods
require a matching `UniqueEntitySlice` type , which we leave for a
follow-up PR.

Because this type will deref into the `UniqueEntitySlice` type, we also
offer the immutable `Vec` methods on this type (which only amount to a
handful). "as inner" functionality is covered by additional
`as_vec`/`as_mut_vec` methods + `AsRef`/`Borrow` trait impls.
Like `UniqueEntityIter::from_iterator_unchecked`, this type has a
`from_vec_unchecked` method as well.

The canonical way to safely obtain this type however is via
`EntitySetIterator::collect_set` or
`UniqueEntityVec::from_entity_set_iter`. Like mentioned in #17513, these
are named suboptimally until supertrait item shadowing arrives, since a
normal `collect` will still run equality checks.
  • Loading branch information
Victoronz authored Jan 28, 2025
1 parent b25bbb7 commit b039bf6
Show file tree
Hide file tree
Showing 3 changed files with 743 additions and 0 deletions.
4 changes: 4 additions & 0 deletions crates/bevy_ecs/clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
disallowed-methods = [
{ path = "UniqueEntityVec::from_iter", reason = "Use UniqueEntityVec::from_entity_set_iter if possible, it skips validation" },
{ path = "EntitySetIterator::collect::<UniqueEntityVec>", reason = "Use EntitySetIterator::collect_set if possible, it skips validation" },
]
4 changes: 4 additions & 0 deletions crates/bevy_ecs/src/entity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ pub use entity_set::*;
pub use map_entities::*;
pub use visit_entities::*;

mod unique_vec;

pub use unique_vec::*;

mod hash;
pub use hash::*;

Expand Down
Loading

0 comments on commit b039bf6

Please sign in to comment.