Skip to content
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

FilteredEntityRef conversions #11838

Merged
merged 8 commits into from
Feb 14, 2024
202 changes: 202 additions & 0 deletions crates/bevy_ecs/src/world/entity_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,58 @@ impl<'a> From<&'a EntityMut<'_>> for EntityRef<'a> {
}
}

impl<'a> TryFrom<FilteredEntityRef<'a>> for EntityRef<'a> {
type Error = ();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does worth it adding an error for this conversion, so users may know why it failed, something like:

pub enum EntityRefAccessError {
    NoReadAll,
    NoWriteAll,
}

impl fmt::Display for EntityRefAccessError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::NoReadAll=> write!(f, "The given FilteredEntityRef has no read access on all entities."),
            Self::NoWriteAll=> write!(f, "The given FilteredEntityRef has no write access on all entities."),
        }
    }
}

impl std::error::Error for IdentifierError {}

Copy link
Contributor Author

@coreh coreh Feb 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the error enum, went with slightly different names to match the naming conventions for existing TryFrom errors, and also for variants of this existing error type


fn try_from(value: FilteredEntityRef<'a>) -> Result<Self, Self::Error> {
if value.access.has_read_all() {
// SAFETY: check above guarantees read-only access to all components of the entity.
Ok(unsafe { EntityRef::new(value.entity) })
} else {
Err(())
}
}
}

impl<'a> TryFrom<&'a FilteredEntityRef<'_>> for EntityRef<'a> {
type Error = ();

fn try_from(value: &'a FilteredEntityRef<'_>) -> Result<Self, Self::Error> {
if value.access.has_read_all() {
// SAFETY: check above guarantees read-only access to all components of the entity.
Ok(unsafe { EntityRef::new(value.entity) })
} else {
Err(())
}
}
}

impl<'a> TryFrom<FilteredEntityMut<'a>> for EntityRef<'a> {
type Error = ();

fn try_from(value: FilteredEntityMut<'a>) -> Result<Self, Self::Error> {
if value.access.has_read_all() {
// SAFETY: check above guarantees read-only access to all components of the entity.
Ok(unsafe { EntityRef::new(value.entity) })
} else {
Err(())
}
}
}

impl<'a> TryFrom<&'a FilteredEntityMut<'_>> for EntityRef<'a> {
type Error = ();

fn try_from(value: &'a FilteredEntityMut<'_>) -> Result<Self, Self::Error> {
if value.access.has_read_all() {
// SAFETY: check above guarantees read-only access to all components of the entity.
Ok(unsafe { EntityRef::new(value.entity) })
} else {
Err(())
}
}
}

/// Provides mutable access to a single entity and all of its components.
///
/// Contrast with [`EntityWorldMut`], which allows adding and removing components,
Expand Down Expand Up @@ -375,6 +427,32 @@ impl<'a> From<&'a mut EntityWorldMut<'_>> for EntityMut<'a> {
}
}

impl<'a> TryFrom<FilteredEntityMut<'a>> for EntityMut<'a> {
type Error = ();

fn try_from(value: FilteredEntityMut<'a>) -> Result<Self, Self::Error> {
if value.access.has_read_all() && value.access.has_write_all() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: AFAIK you don't have to check for read all access, since it's impossible to have write_all access without read_all

Suggested change
if value.access.has_read_all() && value.access.has_write_all() {
if value.access.has_write_all() {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to keep this in as a failsafe.

Copy link
Contributor Author

@coreh coreh Feb 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this guarantee something that we want to match the semantics of & and &mut in Rust, to keep it straightforward, or is it possible that we might want to have write-only access in the future?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's possible that we have write only access to certain data in the future. And more importantly, users can currently do this on their own.

// SAFETY: check above guarantees exclusive access to all components of the entity.
Ok(unsafe { EntityMut::new(value.entity) })
} else {
Err(())
}
}
}

impl<'a> TryFrom<&'a mut FilteredEntityMut<'_>> for EntityMut<'a> {
type Error = ();

fn try_from(value: &'a mut FilteredEntityMut<'_>) -> Result<Self, Self::Error> {
if value.access.has_read_all() && value.access.has_write_all() {
// SAFETY: check above guarantees exclusive access to all components of the entity.
Ok(unsafe { EntityMut::new(value.entity) })
} else {
Err(())
}
}
}

/// A mutable reference to a particular [`Entity`], and the entire world.
/// This is essentially a performance-optimized `(Entity, &mut World)` tuple,
/// which caches the [`EntityLocation`] to reduce duplicate lookups.
Expand Down Expand Up @@ -1671,6 +1749,78 @@ impl<'a> From<&'a FilteredEntityMut<'_>> for FilteredEntityRef<'a> {
}
}

impl<'a> From<EntityRef<'a>> for FilteredEntityRef<'a> {
fn from(entity: EntityRef<'a>) -> Self {
// SAFETY:
// - `EntityRef` guarantees exclusive access to all components in the new `FilteredEntityRef`.
unsafe {
let mut access = Access::default();
access.read_all();
FilteredEntityRef::new(entity.0, access)
}
}
}

impl<'a> From<&'a EntityRef<'_>> for FilteredEntityRef<'a> {
fn from(entity: &'a EntityRef<'_>) -> Self {
// SAFETY:
// - `EntityRef` guarantees exclusive access to all components in the new `FilteredEntityRef`.
unsafe {
let mut access = Access::default();
access.read_all();
FilteredEntityRef::new(entity.0, access)
}
}
}

impl<'a> From<EntityMut<'a>> for FilteredEntityRef<'a> {
fn from(entity: EntityMut<'a>) -> Self {
// SAFETY:
// - `EntityMut` guarantees exclusive access to all components in the new `FilteredEntityRef`.
unsafe {
let mut access = Access::default();
access.read_all();
FilteredEntityRef::new(entity.0, access)
}
}
}

impl<'a> From<&'a EntityMut<'_>> for FilteredEntityRef<'a> {
fn from(entity: &'a EntityMut<'_>) -> Self {
// SAFETY:
// - `EntityMut` guarantees exclusive access to all components in the new `FilteredEntityRef`.
unsafe {
let mut access = Access::default();
access.read_all();
FilteredEntityRef::new(entity.0, access)
}
}
}

impl<'a> From<EntityWorldMut<'a>> for FilteredEntityRef<'a> {
fn from(entity: EntityWorldMut<'a>) -> Self {
// SAFETY:
// - `EntityWorldMut` guarantees exclusive access to the entire world.
unsafe {
let mut access = Access::default();
access.read_all();
FilteredEntityRef::new(entity.into_unsafe_entity_cell(), access)
}
}
}

impl<'a> From<&'a EntityWorldMut<'_>> for FilteredEntityRef<'a> {
fn from(entity: &'a EntityWorldMut<'_>) -> Self {
// SAFETY:
// - `EntityWorldMut` guarantees exclusive access to the entire world.
unsafe {
let mut access = Access::default();
access.read_all();
FilteredEntityRef::new(entity.as_unsafe_entity_cell_readonly(), access)
}
}
}

/// Provides mutable access to a single entity and some of its components defined by the contained [`Access`].
pub struct FilteredEntityMut<'w> {
entity: UnsafeEntityCell<'w>,
Expand Down Expand Up @@ -1848,6 +1998,58 @@ impl<'w> FilteredEntityMut<'w> {
}
}

impl<'a> From<EntityMut<'a>> for FilteredEntityMut<'a> {
fn from(entity: EntityMut<'a>) -> Self {
// SAFETY:
// - `EntityMut` guarantees exclusive access to all components in the new `FilteredEntityMut`.
unsafe {
let mut access = Access::default();
access.read_all();
access.write_all();
FilteredEntityMut::new(entity.0, access)
}
}
}

impl<'a> From<&'a mut EntityMut<'_>> for FilteredEntityMut<'a> {
fn from(entity: &'a mut EntityMut<'_>) -> Self {
// SAFETY:
// - `EntityMut` guarantees exclusive access to all components in the new `FilteredEntityMut`.
unsafe {
let mut access = Access::default();
access.read_all();
access.write_all();
FilteredEntityMut::new(entity.0, access)
}
}
}

impl<'a> From<EntityWorldMut<'a>> for FilteredEntityMut<'a> {
fn from(entity: EntityWorldMut<'a>) -> Self {
// SAFETY:
// - `EntityWorldMut` guarantees exclusive access to the entire world.
unsafe {
let mut access = Access::default();
access.read_all();
access.write_all();
FilteredEntityMut::new(entity.into_unsafe_entity_cell(), access)
}
}
}

impl<'a> From<&'a mut EntityWorldMut<'_>> for FilteredEntityMut<'a> {
fn from(entity: &'a mut EntityWorldMut<'_>) -> Self {
// SAFETY:
// - `EntityWorldMut` guarantees exclusive access to the entire world.
unsafe {
let mut access = Access::default();
access.read_all();
access.write_all();
FilteredEntityMut::new(entity.as_unsafe_entity_cell(), access)
}
}
}

/// Inserts a dynamic [`Bundle`] into the entity.
///
/// # Safety
Expand Down