Skip to content

Commit 862e285

Browse files
add MutUntyped::to_typed
1 parent 7540328 commit 862e285

File tree

1 file changed

+62
-0
lines changed

1 file changed

+62
-0
lines changed

crates/bevy_ecs/src/change_detection.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,30 @@ impl<'a> MutUntyped<'a> {
373373
pub fn into_inner(self) -> PtrMut<'a> {
374374
self.value
375375
}
376+
377+
/// Turn this [`MutUntyped`] into a [`Mut`] by mapping the inner [`PtrMut`] to another value.
378+
/// If you know the type of the value you can do
379+
/// ```no_run
380+
/// # use bevy_ecs::change_detection::{Mut, MutUntyped};
381+
/// # let mut_untyped: MutUntyped = unimplemented!();
382+
/// // SAFETY: ptr is of type `u8`
383+
/// mut_untyped.to_typed(|ptr| unsafe { ptr.deref_mut::<u8>() });
384+
/// ```
385+
/// If you have a [`ReflectFromPtr`](bevy_reflect::ReflectFromPtr) that you know belongs to this [`MutUntyped`],
386+
/// you can do
387+
/// ```no_run
388+
/// # use bevy_ecs::change_detection::{Mut, MutUntyped};
389+
/// # let mut_untyped: MutUntyped = unimplemented!();
390+
/// # let reflect_from_ptr: bevy_reflect::ReflectFromPtr = unimplemented!();
391+
/// // SAFETY: from the context it is known that `ReflectFromPtr` was made for the type of the `MutUntyped`
392+
/// mut_untyped.to_typed(|ptr| unsafe { reflect_from_ptr.as_reflect_ptr_mut(ptr) });
393+
/// ```
394+
pub fn to_typed<T: ?Sized>(self, f: impl FnOnce(PtrMut<'a>) -> &'a mut T) -> Mut<'a, T> {
395+
Mut {
396+
value: f(self.value),
397+
ticks: self.ticks,
398+
}
399+
}
376400
}
377401

378402
impl<'a> DetectChanges for MutUntyped<'a> {
@@ -425,7 +449,11 @@ impl std::fmt::Debug for MutUntyped<'_> {
425449

426450
#[cfg(test)]
427451
mod tests {
452+
use std::ptr::NonNull;
453+
428454
use bevy_ecs_macros::Resource;
455+
use bevy_ptr::PtrMut;
456+
use bevy_reflect::{FromType, ReflectFromPtr};
429457

430458
use crate::{
431459
self as bevy_ecs,
@@ -438,6 +466,8 @@ mod tests {
438466
world::World,
439467
};
440468

469+
use super::MutUntyped;
470+
441471
#[derive(Component)]
442472
struct C;
443473

@@ -612,4 +642,36 @@ mod tests {
612642
// Modifying one field of a component should flag a change for the entire component.
613643
assert!(component_ticks.is_changed(last_change_tick, change_tick));
614644
}
645+
646+
#[test]
647+
fn mut_untyped_to_reflect() {
648+
let mut component_ticks = ComponentTicks {
649+
added: 1,
650+
changed: 2,
651+
};
652+
let ticks = Ticks {
653+
component_ticks: &mut component_ticks,
654+
last_change_tick: 3,
655+
change_tick: 4,
656+
};
657+
658+
let mut value: i32 = 5;
659+
let value = MutUntyped {
660+
// SAFETY: lifetime does not exceed `value`
661+
value: unsafe { PtrMut::new(NonNull::new(&mut value as *mut i32 as *mut u8).unwrap()) },
662+
ticks,
663+
};
664+
665+
let reflect_from_ptr = <ReflectFromPtr as FromType<i32>>::from_type();
666+
667+
let mut new = value.to_typed(|ptr| {
668+
// SAFETY: ptr has type of ReflectFromPtr
669+
let value = unsafe { reflect_from_ptr.as_reflect_ptr_mut(ptr) };
670+
value
671+
});
672+
673+
new.reflect_mut();
674+
675+
assert_eq!(component_ticks.changed, 4);
676+
}
615677
}

0 commit comments

Comments
 (0)