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<A>` to `Ref<B>`.

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<T>` into a `Ref<dyn Reflect>`, 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>
This commit is contained in:
Nicola Papale 2023-06-11 01:19:52 +02:00 committed by GitHub
parent 83de94f9f9
commit 50bc785c8a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -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<U>(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>