@@ -373,6 +373,30 @@ impl<'a> MutUntyped<'a> {
373
373
pub fn into_inner ( self ) -> PtrMut < ' a > {
374
374
self . value
375
375
}
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
+ }
376
400
}
377
401
378
402
impl < ' a > DetectChanges for MutUntyped < ' a > {
@@ -425,7 +449,11 @@ impl std::fmt::Debug for MutUntyped<'_> {
425
449
426
450
#[ cfg( test) ]
427
451
mod tests {
452
+ use std:: ptr:: NonNull ;
453
+
428
454
use bevy_ecs_macros:: Resource ;
455
+ use bevy_ptr:: PtrMut ;
456
+ use bevy_reflect:: { FromType , ReflectFromPtr } ;
429
457
430
458
use crate :: {
431
459
self as bevy_ecs,
@@ -438,6 +466,8 @@ mod tests {
438
466
world:: World ,
439
467
} ;
440
468
469
+ use super :: MutUntyped ;
470
+
441
471
#[ derive( Component ) ]
442
472
struct C ;
443
473
@@ -612,4 +642,36 @@ mod tests {
612
642
// Modifying one field of a component should flag a change for the entire component.
613
643
assert ! ( component_ticks. is_changed( last_change_tick, change_tick) ) ;
614
644
}
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
+ }
615
677
}
0 commit comments