Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Add a method for mapping Mut<T> -> Mut<U> #6199

Closed
wants to merge 10 commits into from
50 changes: 46 additions & 4 deletions crates/bevy_ecs/src/change_detection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ macro_rules! change_detection_impl {
};
}

macro_rules! impl_into_inner {
macro_rules! impl_methods {
($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {
impl<$($generics),* : ?Sized $(+ $traits)?> $name<$($generics),*> {
/// Consume `self` and return a mutable reference to the
Expand All @@ -173,6 +173,17 @@ macro_rules! impl_into_inner {
self.set_changed();
self.value
}

/// Maps to an inner value by applying a function to the contained reference, without flagging a change.
///
/// You must *not* modify the argument passed to the closure.
JoJoJet marked this conversation as resolved.
Show resolved Hide resolved
/// Violating this rule will likely result in logic errors, but it will not cause undefined behavior.
pub fn map_unchanged<U: ?Sized>(self, f: impl FnOnce(&mut $target) -> &mut U) -> Mut<'a, U> {
JoJoJet marked this conversation as resolved.
Show resolved Hide resolved
Mut {
value: f(self.value),
ticks: self.ticks,
}
}
}
};
}
Expand Down Expand Up @@ -215,7 +226,7 @@ pub struct ResMut<'a, T: ?Sized + Resource> {
}

change_detection_impl!(ResMut<'a, T>, T, Resource);
impl_into_inner!(ResMut<'a, T>, T, Resource);
impl_methods!(ResMut<'a, T>, T, Resource);
impl_debug!(ResMut<'a, T>, Resource);

impl<'a, T: Resource> From<ResMut<'a, T>> for Mut<'a, T> {
Expand Down Expand Up @@ -247,7 +258,7 @@ pub struct NonSendMut<'a, T: ?Sized + 'static> {
}

change_detection_impl!(NonSendMut<'a, T>, T,);
impl_into_inner!(NonSendMut<'a, T>, T,);
impl_methods!(NonSendMut<'a, T>, T,);
impl_debug!(NonSendMut<'a, T>,);

impl<'a, T: 'static> From<NonSendMut<'a, T>> for Mut<'a, T> {
Expand All @@ -268,7 +279,7 @@ pub struct Mut<'a, T: ?Sized> {
}

change_detection_impl!(Mut<'a, T>, T,);
impl_into_inner!(Mut<'a, T>, T,);
impl_methods!(Mut<'a, T>, T,);
impl_debug!(Mut<'a, T>,);

/// Unique mutable borrow of resources or an entity's component.
Expand Down Expand Up @@ -497,4 +508,35 @@ mod tests {
assert_eq!(3, into_mut.ticks.last_change_tick);
assert_eq!(4, into_mut.ticks.change_tick);
}

#[test]
fn map_mut() {
use super::*;
struct Outer(i64);

let mut component_ticks = ComponentTicks {
added: 1,
changed: 2,
};
let ticks = Ticks {
component_ticks: &mut component_ticks,
last_change_tick: 2,
change_tick: 3,
};

let mut outer = Outer(0);
let ptr = Mut {
value: &mut outer,
ticks,
};
assert!(!ptr.is_changed());

// Perform a mapping operation.
let mut inner = ptr.map_unchanged(|x| &mut x.0);
assert!(!inner.is_changed());

// Mutate the inner value.
*inner = 64;
assert!(inner.is_changed());
JoJoJet marked this conversation as resolved.
Show resolved Hide resolved
}
}