bevy
bevy copied to clipboard
Add `get_ref` methods for Queries
Objective
- fix #11517 by adding a
get_refmethod forQueryandQueryState
Solution
- Add
get_refmethod forQueryState - Add
get_refmethod forQuery
Changelog
- The
QueryDatatrait has another associated type -ReadOnlySmartRef.ReadOnlySmartRefis the type we get when we request read-only, change-detection-enabled, shared access to the data. For example: access of&TisRef<T>, the same is true for&mut T, because this access is explicitly read-only, and as such is analogous to first converting aQueryDatato its read-only form (QueryData::ReadOnly) and then to itsQueryData::ReadOnlySmartRefform. - The
QueryDataderive macro changed a bit in its implementation to reflect the above changes. - The
QueryDataimplementations of many types have changed to reflect the above changes. get_ref,get_component_ref,iter_ref,as_readonly_smart_refmethods were added toQueryStateget_ref,iter_ref,iter_combinations_ref,iter_many_ref,par_iter_ref,get_component_ref,component_ref,single_ref,get_single_ref, methods was added toQuerysystem_query_get_reftest was added to make sureQuery<Ref<T>>andQuery<&T>+get_refproduce the same result.
Migration guide
- An associated type was added to the public
QueryDatatrait:ReadOnlySmartRef. If the type you're implementingQueryDatafor can be accessed using some change-detection-enabled, read-only way, for example: theRefsmart pointer - then defineReadOnlySmartRefto be that type. If it can't be accessed that way, then keepReadOnlySmartRefthe same asReadOnly. For example:
// Before:
impl<'a, C: Component> QueryData for &'a C {
type ReadOnly = &'a C; // Regular read-only access
}
impl QueryData for Entity {
type ReadOnly = Entity; // Standard read-only access of a Copy type
}
// After:
impl<'a, C: Component> QueryData for &'a C {
type ReadOnly = &'a C; // Regular read-only access
type ReadOnlySmartRef = Ref<'a, C>; // We can use `Ref` for change detection
}
impl QueryData for Entity {
type ReadOnly = Entity; // Standard read-only access of a Copy type
type ReadOnlySmartRef = Entity; // No option for change-detection, so keep it the same as `Self::ReadOnly`
}
@alice-i-cecile What do I do about that one failing CI that expects a certain error message? (because the derive macro changed a bit and that messed with the error)
@alice-i-cecile What do I do about that one failing CI that expects a certain error message? (because the derive macro changed a bit and that messed with the error)
The error message should be updated to match in this PR. IIRC there's a "bless" command to do it automatically if you prefer: check the docs of the crate we're using if you want to try it out :)
It looks like your PR is a breaking change, but you didn't provide a migration guide.
Could you add some context on what users should update when this change get released in a new version of Bevy?
It will be used to help writing the migration guide for the version. Putting it after a ## Migration Guide will help it get automatically picked up by our tooling.
Naming suggestions:
QueryItemRef(Mutis often used as a suffix, so why notRef?)ValueRefWithRefRefWrapperRefAccessorRefAccessReference
Is there a reason it's not possible for the "reffed" form of &'a mut C to be Mut<'a, C>? That would be my intuitive expectation, to just add change-detection without changing the rest of the semantics.
The default form of &'a mut C already is Mut<'a, C> 🤔 I think that "the reffed form is always read-only" is more clear, and works better with the rest of the API.
I settled on ROQueryItemRef and ReadOnlySmartRef, I think these are consistent and straightforward. I implemented many of your suggestions in the documentation, and added a migration guide.
Should I add the missing "get_ref API" such as iter_ref etc. here or in another PR? or not at all?
I'm unsure of the utility of this PR. I feel like if you have a Query<&T> it's easy enough to change that to a Query<Ref<T>>. So the only time this comes in handy is when you have a Query<&mut T>. I feel like it would be easier to change the ReadOnly associated type to a Ref<T> for impl WorldData for &mut T. It would be a breaking change, but I think it makes sense for Mut<T> to go to a Ref<T> when it's read only instead of a &T. This would make query.get return a Ref<T> for Query<&mut T>.
I'm also going to voice my opposition to this, quite frankly, significant complexity of the implementation and how much utility it provides. Regarding access to Ref<T> from Query<&mut T>, another option would be to allow Mut<T> to downgrade into a Ref<T> for a similar effect.
I agree, I've felt this is too much for too little.