Skip to content

Conversation

@cBournhonesque
Copy link
Contributor

Objective

#21601 introduced helpers to dynamically access relationship values
The RelationshipAccessor can be hard to use manually as it requires manipulating pointers; we can provide helper functions in UnsafeEntityCell and friends to access those.

cBournhonesque and others added 5 commits October 23, 2025 10:52
Change-Id: I0fc5f429189a9a55c233ac353cdb0767ed1502e6
fmt
Change-Id: If524fee3136c91872238be8bb005bfd726aa8ab4
@cBournhonesque cBournhonesque added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Oct 23, 2025
@Victoronz
Copy link
Contributor

Is there a reason why these functions return Box<dyn Iterator> instead of impl Iterator?
Also I am curious how it would error if the '_ are removed, given how use<'a> became a thing recently.

@cBournhonesque
Copy link
Contributor Author

Is there a reason why these functions return Box<dyn Iterator> instead of impl Iterator? Also I am curious how it would error if the '_ are removed, given how use<'a> became a thing recently.

You're right, impl Iterator is much better! will change

Comment on lines +708 to +716
) -> Option<impl Iterator<Item = Entity> + use<'w>> {
self.access
.has_component_read(relationship_target_id)
// SAFETY: We have read access
.then(|| unsafe {
self.entity
.get_relationship_targets_by_id(relationship_target_id)
})
.flatten()
Copy link
Contributor

Choose a reason for hiding this comment

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

Why can't this be self.as_readonly().get_relationship_targets_by_id(relationship_target_id), like get_relationship_by_id is? ... Oh, I see, that would borrow from a temporary. Wait, then how do we do that elsewhere? ... Hmm, I think some of the lifetimes are wrong here.

The read-only methods on mutable EntityRefs need to return things that borrow from '_ instead of 'w, since otherwise you could do

let borrows_r1 = filtered_entity.get_relationship_targets_by_id(r1);
let borrows_r1_mutably = filtered_entity.get_mut_by_id(r1);
// Both variables are alive here, because the lifetime in `borrows_r1`
// wasn't tied to the borrow of `filtered_entity`!

(If we want methods that return 'w, they need to consume self, like into_mut() does. We don't need those here, though, since they're uncommon and users can build them out of entity_ref.into_readonly().get_relationship_targets_by_id().)

So, I think this should be:

Suggested change
) -> Option<impl Iterator<Item = Entity> + use<'w>> {
self.access
.has_component_read(relationship_target_id)
// SAFETY: We have read access
.then(|| unsafe {
self.entity
.get_relationship_targets_by_id(relationship_target_id)
})
.flatten()
) -> Option<impl Iterator<Item = Entity> + use<'_>> {
self.as_readonly().get_relationship_targets_by_id(relationship_target_id)

pub fn get_relationship_targets_by_id(
&self,
relationship_target_id: ComponentId,
) -> Option<impl Iterator<Item = Entity> + use<'_>> {
Copy link
Contributor

Choose a reason for hiding this comment

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

In contrast, EntityRef::get_relationship_targets_by_id can use 'w, because EntityRef knows nothing else can have mutable access for the rest of the 'w lifetime. So this can be:

Suggested change
) -> Option<impl Iterator<Item = Entity> + use<'_>> {
) -> Option<impl Iterator<Item = Entity> + use<'w>> {

Comment on lines +621 to +622
// SAFETY: We have read-only access to all components of this entity.
unsafe { self.cell.get_relationship_by_id(relationship_id) }
Copy link
Contributor

Choose a reason for hiding this comment

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

And I'd recommend writing these in terms of as_readonly(), like we do with EntityMut::get(), since that means we can write them with safe code and not have to check as many lifetimes manually:

Suggested change
// SAFETY: We have read-only access to all components of this entity.
unsafe { self.cell.get_relationship_by_id(relationship_id) }
self.as_readonly().get_relationship_by_id(relationship_id)

Comment on lines +655 to +659
// SAFETY: We have read-only access to all components of this entity.
unsafe {
self.cell
.get_relationship_targets_by_id(relationship_target_id)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// SAFETY: We have read-only access to all components of this entity.
unsafe {
self.cell
.get_relationship_targets_by_id(relationship_target_id)
}
self.as_readonly().get_relationship_targets_by_id(relationship_target_id)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants