diff --git a/rfcs/71-write-only-world-query.md b/rfcs/71-write-only-world-query.md new file mode 100644 index 00000000..5ecfa03b --- /dev/null +++ b/rfcs/71-write-only-world-query.md @@ -0,0 +1,385 @@ +# `WriteOnly` world query + +## Summary + +Add a `WriteOnly` `WorldQuery`. `WriteOnly` acts like `&mut`, but tells bevy +that the value of component `T` is overwritten unconditionally by the system. +Add the `B0006` error. This tells users that they are modifying a component +that is later overwritten unconditionally. + +## Motivation + +When I write to the `Transform` of a UI node, it doesn't do anything +and I see no error messages. + +Why this happens is fairly trivial: `Transform` is updated by the UI system +in `PostUpdate`, and overwrites the user-set value unconditionally. + +This is also true of `AnimationClip`s. A system overwrites bone transforms +targeted by an animations in `PostUpdate`. Even if the `AnimationClip` is +paused! + +## User-facing explanation + +### A new `WorldQuery` + +We introduce a new `WorldQuery`: `WriteOnly`. + +```rust +struct WriteOnly<'a, T>(Mut<'a, T>); + +impl<'a, T> WriteOnly<'a, T> { + pub fn set(&mut self, value: T) { + *self.0 = value + } + /// Only **write** to the returned reference. + /// If you have a `Vec`, for example, you should not leave any of the + /// previously existing value. + pub fn ref_mut(&mut self) -> Mut { + self.0.reborrow() + } + pub fn into_inner(self) -> Mut<'a, T> { + self.0 + } +} + +``` + +`WriteOnly` is very much nothing more than a thin wrapper around `Mut`. +With one significant difference: semantically, it cannot be read. + +We promise to the scheduler that all values in the `Query>` will +be overwritten, with no traces of its previous value. + +### Write-only access + +We add a new method to the `WorldQuery` trait. + +```rust + fn update_write_only_component_access(state: &Self::State, access: &mut FilteredAccess) {} +``` + +Notice that β€” unlike `update_component_access` β€” it has a default implementation. +It is not unsound to erronously populate this `FilteredAccess`. It is just used +as a hint for triggering error messages. + +This adds another `FilteredAccess` to the query. This `FilteredAccess` +is only written-to and read-from at app initialization when setting up the systems. + +### Triggering the warning + +Consider `ui_layout_system`: + +https://github.com/bevyengine/bevy/blob/469a19c290861049ad121aa83d72f830d1ec9b40/crates/bevy_ui/src/layout/mod.rs#L215-L230 + +```rust +pub fn ui_layout_system( + primary_window: Query<(Entity, &Window), With>, + windows: Query<(Entity, &Window)>, + ui_scale: Res, + mut scale_factor_events: EventReader, + mut resize_events: EventReader, + mut ui_surface: ResMut, + root_node_query: Query, Without)>, + style_query: Query<(Entity, Ref