-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
General entity set cleanup #21498
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
General entity set cleanup #21498
Changes from all commits
513da58
4e08725
bb95355
dceec17
68546a9
1f1d205
a1530b0
3c0c67a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,7 @@ use core::{ | |
fmt::{Debug, Formatter}, | ||
hash::{BuildHasher, Hash}, | ||
iter::{self, FusedIterator}, | ||
option, result, | ||
option, ptr, result, | ||
}; | ||
|
||
use super::{Entity, UniqueEntityEquivalentSlice}; | ||
|
@@ -58,7 +58,7 @@ pub trait ContainsEntity { | |
/// To obtain hash values forming the same total order as [`Entity`], any [`Hasher`] used must be | ||
/// deterministic and concerning [`Entity`], collisionless. | ||
/// Standard library hash collections handle collisions with an [`Eq`] fallback, but do not account for | ||
/// determinism when [`BuildHasher`] is unspecified,. | ||
/// determinism when [`BuildHasher`] is unspecified. | ||
/// | ||
/// [`Hash`]: core::hash::Hash | ||
/// [`Hasher`]: core::hash::Hasher | ||
|
@@ -68,6 +68,7 @@ pub trait ContainsEntity { | |
pub unsafe trait EntityEquivalent: ContainsEntity + Eq {} | ||
|
||
impl ContainsEntity for Entity { | ||
#[inline] | ||
fn entity(&self) -> Entity { | ||
*self | ||
} | ||
|
@@ -78,6 +79,7 @@ impl ContainsEntity for Entity { | |
unsafe impl EntityEquivalent for Entity {} | ||
|
||
impl<T: ContainsEntity> ContainsEntity for &T { | ||
#[inline] | ||
fn entity(&self) -> Entity { | ||
(**self).entity() | ||
} | ||
|
@@ -90,6 +92,7 @@ impl<T: ContainsEntity> ContainsEntity for &T { | |
unsafe impl<T: EntityEquivalent> EntityEquivalent for &T {} | ||
|
||
impl<T: ContainsEntity> ContainsEntity for &mut T { | ||
#[inline] | ||
fn entity(&self) -> Entity { | ||
(**self).entity() | ||
} | ||
|
@@ -102,6 +105,7 @@ impl<T: ContainsEntity> ContainsEntity for &mut T { | |
unsafe impl<T: EntityEquivalent> EntityEquivalent for &mut T {} | ||
|
||
impl<T: ContainsEntity> ContainsEntity for Box<T> { | ||
#[inline] | ||
fn entity(&self) -> Entity { | ||
(**self).entity() | ||
} | ||
|
@@ -114,6 +118,7 @@ impl<T: ContainsEntity> ContainsEntity for Box<T> { | |
unsafe impl<T: EntityEquivalent> EntityEquivalent for Box<T> {} | ||
|
||
impl<T: ContainsEntity> ContainsEntity for Rc<T> { | ||
#[inline] | ||
fn entity(&self) -> Entity { | ||
(**self).entity() | ||
} | ||
|
@@ -126,6 +131,7 @@ impl<T: ContainsEntity> ContainsEntity for Rc<T> { | |
unsafe impl<T: EntityEquivalent> EntityEquivalent for Rc<T> {} | ||
|
||
impl<T: ContainsEntity> ContainsEntity for Arc<T> { | ||
#[inline] | ||
fn entity(&self) -> Entity { | ||
(**self).entity() | ||
} | ||
|
@@ -143,12 +149,12 @@ unsafe impl<T: EntityEquivalent> EntityEquivalent for Arc<T> {} | |
/// As a consequence, [`into_iter()`] on `EntitySet` will always produce another `EntitySet`. | ||
/// | ||
/// Implementing this trait allows for unique query iteration over a list of entities. | ||
/// See [`iter_many_unique`] and [`iter_many_unique_mut`] | ||
/// See [`iter_many_unique`] and [`iter_many_unique_mut`]. | ||
/// | ||
/// Note that there is no guarantee of the [`IntoIterator`] impl being deterministic, | ||
/// it might return different iterators when called multiple times. | ||
/// Neither is there a guarantee that the comparison trait impls of `EntitySet` match that | ||
/// of the respective [`EntitySetIterator`] (or of a [`Vec`] collected from its elements) | ||
/// of the respective [`EntitySetIterator`] (or of a [`Vec`] collected from its elements). | ||
/// | ||
/// [`Self::IntoIter`]: IntoIterator::IntoIter | ||
/// [`into_iter()`]: IntoIterator::into_iter | ||
|
@@ -342,6 +348,7 @@ pub trait FromEntitySetIterator<A: EntityEquivalent>: FromIterator<A> { | |
impl<T: EntityEquivalent + Hash, S: BuildHasher + Default> FromEntitySetIterator<T> | ||
for HashSet<T, S> | ||
{ | ||
#[inline] | ||
fn from_entity_set_iter<I: EntitySet<Item = T>>(set_iter: I) -> Self { | ||
let iter = set_iter.into_iter(); | ||
let set = HashSet::<T, S>::with_capacity_and_hasher(iter.size_hint().0, S::default()); | ||
|
@@ -358,14 +365,17 @@ impl<T: EntityEquivalent + Hash, S: BuildHasher + Default> FromEntitySetIterator | |
/// An iterator that yields unique entities. | ||
/// | ||
/// This wrapper can provide an [`EntitySetIterator`] implementation when an instance of `I` is known to uphold uniqueness. | ||
#[repr(transparent)] | ||
pub struct UniqueEntityIter<I: Iterator<Item: EntityEquivalent>> { | ||
iter: I, | ||
} | ||
|
||
impl<I: EntitySetIterator> UniqueEntityIter<I> { | ||
/// Constructs a `UniqueEntityIter` from an [`EntitySetIterator`]. | ||
pub fn from_entity_set_iterator<S>(iter: I) -> Self { | ||
Self { iter } | ||
#[inline] | ||
pub const fn from_entity_set_iter(iter: I) -> Self { | ||
// SAFETY: iter implements `EntitySetIterator`. | ||
unsafe { Self::from_iter_unchecked(iter) } | ||
} | ||
} | ||
|
||
|
@@ -375,17 +385,42 @@ impl<I: Iterator<Item: EntityEquivalent>> UniqueEntityIter<I> { | |
/// # Safety | ||
/// `iter` must only yield unique elements. | ||
/// As in, the resulting iterator must adhere to the safety contract of [`EntitySetIterator`]. | ||
pub unsafe fn from_iterator_unchecked(iter: I) -> Self { | ||
#[inline] | ||
pub const unsafe fn from_iter_unchecked(iter: I) -> Self { | ||
Self { iter } | ||
} | ||
|
||
/// Constructs a [`UniqueEntityIter`] from an iterator unsafely. | ||
/// | ||
/// # Safety | ||
/// `iter` must only yield unique elements. | ||
/// As in, the resulting iterator must adhere to the safety contract of [`EntitySetIterator`]. | ||
#[inline] | ||
pub const unsafe fn from_iter_ref_unchecked(iter: &I) -> &Self { | ||
// SAFETY: UniqueEntityIter is a transparent wrapper around I. | ||
unsafe { &*ptr::from_ref(iter).cast() } | ||
} | ||
|
||
/// Constructs a [`UniqueEntityIter`] from an iterator unsafely. | ||
/// | ||
/// # Safety | ||
/// `iter` must only yield unique elements. | ||
/// As in, the resulting iterator must adhere to the safety contract of [`EntitySetIterator`]. | ||
#[inline] | ||
pub const unsafe fn from_iter_mut_unchecked(iter: &mut I) -> &mut Self { | ||
// SAFETY: UniqueEntityIter is a transparent wrapper around I. | ||
unsafe { &mut *ptr::from_mut(iter).cast() } | ||
} | ||
|
||
/// Returns the inner `I`. | ||
#[inline] | ||
pub fn into_inner(self) -> I { | ||
self.iter | ||
} | ||
|
||
/// Returns a reference to the inner `I`. | ||
pub fn as_inner(&self) -> &I { | ||
#[inline] | ||
pub const fn as_inner(&self) -> &I { | ||
&self.iter | ||
} | ||
|
||
|
@@ -395,18 +430,21 @@ impl<I: Iterator<Item: EntityEquivalent>> UniqueEntityIter<I> { | |
/// | ||
/// `self` must always contain an iterator that yields unique elements, | ||
/// even while this reference is live. | ||
pub unsafe fn as_mut_inner(&mut self) -> &mut I { | ||
#[inline] | ||
pub const unsafe fn as_mut_inner(&mut self) -> &mut I { | ||
&mut self.iter | ||
} | ||
} | ||
|
||
impl<I: Iterator<Item: EntityEquivalent>> Iterator for UniqueEntityIter<I> { | ||
type Item = I::Item; | ||
|
||
#[inline] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't make sense to inline non-public functions or functions with generics. I'd add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, seems like Judging by the discussions around inline, I wouldn't say that it doesn't make sense to inline non-public functions or functions with generics, but that it makes sense less often. Given this, I think I'll cull most There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The linked article explains it quite nicely. Trivial private functions can already be inlined because the compiler sees their bodies during crate compilation. We even got a conservative cross-crate inlining recently. Generics are monomorphized during compilation, so their bodies are also visible during crate compilation, which means the compiler can inline them as well. It would be annoying to manually add It's like unrolling loops - we don't do this anymore, unless profiling shows that it's necessary for humans to step in. |
||
fn next(&mut self) -> Option<Self::Item> { | ||
self.iter.next() | ||
} | ||
|
||
#[inline] | ||
fn size_hint(&self) -> (usize, Option<usize>) { | ||
self.iter.size_hint() | ||
} | ||
|
@@ -415,6 +453,7 @@ impl<I: Iterator<Item: EntityEquivalent>> Iterator for UniqueEntityIter<I> { | |
impl<I: ExactSizeIterator<Item: EntityEquivalent>> ExactSizeIterator for UniqueEntityIter<I> {} | ||
|
||
impl<I: DoubleEndedIterator<Item: EntityEquivalent>> DoubleEndedIterator for UniqueEntityIter<I> { | ||
#[inline] | ||
fn next_back(&mut self) -> Option<Self::Item> { | ||
self.iter.next_back() | ||
} | ||
|
@@ -426,6 +465,7 @@ impl<I: FusedIterator<Item: EntityEquivalent>> FusedIterator for UniqueEntityIte | |
unsafe impl<I: Iterator<Item: EntityEquivalent>> EntitySetIterator for UniqueEntityIter<I> {} | ||
|
||
impl<T, I: Iterator<Item: EntityEquivalent> + AsRef<[T]>> AsRef<[T]> for UniqueEntityIter<I> { | ||
#[inline] | ||
fn as_ref(&self) -> &[T] { | ||
self.iter.as_ref() | ||
} | ||
|
@@ -434,6 +474,7 @@ impl<T, I: Iterator<Item: EntityEquivalent> + AsRef<[T]>> AsRef<[T]> for UniqueE | |
impl<T: EntityEquivalent, I: Iterator<Item: EntityEquivalent> + AsRef<[T]>> | ||
AsRef<UniqueEntityEquivalentSlice<T>> for UniqueEntityIter<I> | ||
{ | ||
#[inline] | ||
fn as_ref(&self) -> &UniqueEntityEquivalentSlice<T> { | ||
// SAFETY: All elements in the original slice are unique. | ||
unsafe { UniqueEntityEquivalentSlice::from_slice_unchecked(self.iter.as_ref()) } | ||
|
@@ -443,6 +484,7 @@ impl<T: EntityEquivalent, I: Iterator<Item: EntityEquivalent> + AsRef<[T]>> | |
impl<T: EntityEquivalent, I: Iterator<Item: EntityEquivalent> + AsMut<[T]>> | ||
AsMut<UniqueEntityEquivalentSlice<T>> for UniqueEntityIter<I> | ||
{ | ||
#[inline] | ||
fn as_mut(&mut self) -> &mut UniqueEntityEquivalentSlice<T> { | ||
// SAFETY: All elements in the original slice are unique. | ||
unsafe { UniqueEntityEquivalentSlice::from_slice_unchecked_mut(self.iter.as_mut()) } | ||
|
@@ -451,6 +493,7 @@ impl<T: EntityEquivalent, I: Iterator<Item: EntityEquivalent> + AsMut<[T]>> | |
|
||
// Default does not guarantee uniqueness, meaning `I` needs to be EntitySetIterator. | ||
impl<I: EntitySetIterator + Default> Default for UniqueEntityIter<I> { | ||
#[inline] | ||
fn default() -> Self { | ||
Self { | ||
iter: Default::default(), | ||
|
@@ -460,6 +503,7 @@ impl<I: EntitySetIterator + Default> Default for UniqueEntityIter<I> { | |
|
||
// Clone does not guarantee to maintain uniqueness, meaning `I` needs to be EntitySetIterator. | ||
impl<I: EntitySetIterator + Clone> Clone for UniqueEntityIter<I> { | ||
#[inline] | ||
fn clone(&self) -> Self { | ||
Self { | ||
iter: self.iter.clone(), | ||
|
@@ -506,7 +550,7 @@ mod tests { | |
|
||
// SAFETY: SpawnBatchIter is `EntitySetIterator`, | ||
let mut unique_entity_iter = | ||
unsafe { UniqueEntityIter::from_iterator_unchecked(spawn_batch.iter()) }; | ||
unsafe { UniqueEntityIter::from_iter_unchecked(spawn_batch.iter()) }; | ||
|
||
let entity_set = unique_entity_iter | ||
.by_ref() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this makes sense for completeness, but there's not much you can do with an
&impl Iterator
.Same thing for
UniqueEntityEquivalentVec::from_vec_ref_unchecked
. Are there ever any cases where you need that instead ofUniqueEntityEquivalentSlice::from_slice_unchecked
? I guess you can callcapacity()
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For iterators, remember that is a
UniqueEntityIter
construction method, its main purpose is to be able to mark any iterator as anEntitySetIterator
. This is not just for iteration itself! Sometimes, there are&SomeIterator
can return underlying views into a collection, like f.e.as_slice()
/AsRef<[T]>
.For
Vec
s, it has to do with safety around the uniqueness invariant:If you know that you have borrowed the full collection, you have stronger guarantees about its subsections.
F.e. a slice can always have adjacent elements you have no awareness of/access to. If you have a
Vec
, this is never the case.Right now, mutable
UniqueEntitySlice
logic is not yet implemented, so we do not yet have safety comments that talk about this subtlety.Interestingly enough, the need to reference collections while maintaining a "no superslice" guarantee is not one I've really heard of before, which seems to be corroborated by some triggered lints surrounding
&Box<[T]>
and the like.