From 50bc785c8a1404ea1bff92c80b27538717fdcc89 Mon Sep 17 00:00:00 2001 From: Nicola Papale Date: Sun, 11 Jun 2023 01:19:52 +0200 Subject: [PATCH] Add `new` and `map` methods to `Ref` (#8797) # Objective `Ref` is a useful way of accessing change detection data. However, unlike `Mut`, it doesn't expose a constructor or even a way to go from `Ref` to `Ref`. Such methods could be useful, for example, to 3rd party crates that want to expose change detection information in a clean way. My use case is to map a `Ref` into a `Ref`, and keep change detection info to avoid running expansive routines. ## Solution We add the `new` and `map` methods. Since similar methods exist on `Mut` where they are much more footgunny to use, I judged that it was acceptable to create such methods. ## Workaround Currently, it's not possible to create/project `Ref`s. One can define their own `Ref` and implement `ChangeDetection` on it. One would then use `ChangeTrackers` to populate the custom `Ref` with tick data. --- ## Changelog - Added the `Ref::map` and `Ref::new` methods for more ergonomic `Ref`s --------- Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> --- crates/bevy_ecs/src/change_detection.rs | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/crates/bevy_ecs/src/change_detection.rs b/crates/bevy_ecs/src/change_detection.rs index 1bf6a22d18..09efcf5c91 100644 --- a/crates/bevy_ecs/src/change_detection.rs +++ b/crates/bevy_ecs/src/change_detection.rs @@ -542,6 +542,46 @@ impl<'a, T: ?Sized> Ref<'a, T> { pub fn into_inner(self) -> &'a T { self.value } + + /// Map `Ref` to a different type using `f`. + /// + /// This doesn't do anything else than call `f` on the wrapped value. + /// This is equivalent to [`Mut::map_unchanged`]. + pub fn map(self, f: impl FnOnce(&T) -> &U) -> Ref<'a, U> { + Ref { + value: f(self.value), + ticks: self.ticks, + } + } + + /// Create a new `Ref` using provided values. + /// + /// This is an advanced feature, `Ref`s are designed to be _created_ by + /// engine-internal code and _consumed_ by end-user code. + /// + /// - `value` - The value wrapped by `Ref`. + /// - `added` - A [`Tick`] that stores the tick when the wrapped value was created. + /// - `changed` - A [`Tick`] that stores the last time the wrapped value was changed. + /// - `last_run` - A [`Tick`], occurring before `this_run`, which is used + /// as a reference to determine whether the wrapped value is newly added or changed. + /// - `this_run` - A [`Tick`] corresponding to the current point in time -- "now". + pub fn new( + value: &'a T, + added: &'a Tick, + changed: &'a Tick, + last_run: Tick, + this_run: Tick, + ) -> Ref<'a, T> { + Ref { + value, + ticks: Ticks { + added, + changed, + last_run, + this_run, + }, + } + } } impl<'w, 'a, T> IntoIterator for &'a Ref<'w, T>