bevy icon indicating copy to clipboard operation
bevy copied to clipboard

add `MutUntyped::map_unchanged`

Open jakobhellermann opened this issue 2 years ago • 19 comments

Objective

MutUntyped is the untyped variant of Mut<T> that stores a PtrMut instead of a &mut T. Working with a MutUntyped is a bit annoying, because as soon you want to use the ptr e.g. as a &mut dyn Reflect you cannot use a type like Mut<dyn Reflect> but instead need to carry around a &mut dyn Reflect and a impl FnMut() to mark the value as changed.

Solution

  • Provide a method map_unchanged to turn a MutUntyped into a Mut<T> by mapping the PtrMut<'a> to a &'a mut T This can be used like this:
// SAFETY: ptr is of type `u8`
let val: Mut<u8> = mut_untyped.map_unchanged(|ptr| unsafe { ptr.deref_mut::<u8>() });

// SAFETY: from the context it is known that `ReflectFromPtr` was made for the type of the `MutUntyped`
let val: Mut<dyn Reflect> = mut_untyped.map_unchanged(|ptr| unsafe { reflect_from_ptr.as_reflect_ptr_mut(ptr) });

Note that nothing prevents you from doing

mut_untyped.map_unchanged(|ptr| &mut ());

or using any other mutable reference you can get, but IMO that is fine since that will only result in a Mut that will dereference to that value and mark the original value as changed. The lifetimes here prevent anything bad from happening.

Alternatives

  1. Make Ticks public and provide a method to get construct a Mut from Ticks and &mut T. More powerful and more easy to misuse.
  2. Do nothing. People can still do everything they want, but they need to pass (&mut dyn Reflect, impl FnMut() + '_) around instead of Mut<dyn Reflect>

Changelog

  • add MutUntyped::map_unchanged to turn a MutUntyped into its typed counterpart

jakobhellermann avatar Oct 31 '22 12:10 jakobhellermann

Definitely opposed to alternate solution 1: it exposes far more internals of change detection than I'd like. I trust you that solution 2 is too annoying.

alice-i-cecile avatar Oct 31 '22 15:10 alice-i-cecile

I'd like to see PR description and title updated + the nit addressed before we merge.

alice-i-cecile avatar Jan 01 '23 02:01 alice-i-cecile

I'd like to see PR description and title updated + the nit addressed before we merge.

Done

jakobhellermann avatar Feb 13 '23 20:02 jakobhellermann

@jakobhellermann once CI is passing I'm comfortable merging this.

alice-i-cecile avatar Feb 13 '23 21:02 alice-i-cecile

CI is green

jakobhellermann avatar Feb 17 '23 11:02 jakobhellermann

@jakobhellermann if you could remove that unsafe here in the next day or two that would be great. I'll be sure to get this in regardless though; we can make a quick follow-up PR.

alice-i-cecile avatar Feb 17 '23 15:02 alice-i-cecile

bors r+

alice-i-cecile avatar Feb 17 '23 15:02 alice-i-cecile

Build failed (retrying...):

bors[bot] avatar Feb 17 '23 15:02 bors[bot]

Build failed:

bors[bot] avatar Feb 17 '23 15:02 bors[bot]

Ah, the suggestion left a dead import around.

alice-i-cecile avatar Feb 17 '23 15:02 alice-i-cecile

bors r-

alice-i-cecile avatar Feb 17 '23 15:02 alice-i-cecile

@jakobhellermann can you fix this up? I'd love to merge this for the release.

alice-i-cecile avatar Feb 20 '23 15:02 alice-i-cecile

The PR description seems very out of date to me, the motivation is basically invalidated by the existence of MutUntyped::with_type which would let you write code as untyped.with_type::<T>().map_unchanged(|data| ...) instead of untyped.map_unchanged::<T>(|data| ...).

I would expect MutUntyped::map_unchanged to not involve giving a type to the pointer at all, i.e. fn map_unchanged(self, f: impl FnOnce(PtrMut<'w>) -> PtrMut<'w>) -> Self

BoxyUwU avatar Feb 20 '23 18:02 BoxyUwU

I would expect MutUntyped::map_unchanged to not involve giving a type to the pointer at all, i.e. fn map_unchanged(self, f: impl FnOnce(PtrMut<'w>) -> PtrMut<'w>) -> Self

What would a completely untyped map operation like that be useful for?

joseph-gio avatar Feb 21 '23 14:02 joseph-gio

you are working with dynamic components and want to map the Mut to go to a field instead of the whole struct maybe

BoxyUwU avatar Feb 21 '23 18:02 BoxyUwU

The PR description seems very out of date to me, the motivation is basically invalidated by the existence of MutUntyped::with_type which would let you write code as untyped.with_type::<T>().map_unchanged(|data| ...) instead of untyped.map_unchanged::<T>(|data| ...).

Your suggestion only works if you know the type T, not if you have another way of going from Ptr to what you want like

let val: Mut<dyn Reflect> = mut_untyped.map_unchanged(|ptr| unsafe { reflect_from_ptr.as_reflect_ptr_mut(ptr) });

I would expect MutUntyped::map_unchanged to not involve giving a type to the pointer at all, i.e. fn map_unchanged(self, f: impl FnOnce(PtrMut<'w>) -> PtrMut<'w>) -> Self

Do you have a name suggestion for what this PR provides? Because it maps the Ptr to anything else. It just doesn't map the pointer to any pointer.

jakobhellermann avatar Feb 26 '23 16:02 jakobhellermann

Your method also only works if you a type T to use?

BoxyUwU avatar Feb 26 '23 17:02 BoxyUwU

If I have a ReflectFromPtr instance I can use that to map the MutUntyped to a Mut<dyn Reflect>. Theoretically, not without this PR.

No ::<T> involved.

jakobhellermann avatar Feb 26 '23 17:02 jakobhellermann

ah I see what you mean, with this you can do with_type like stuff that requires more than just .deref/.deref_mut

BoxyUwU avatar Feb 26 '23 17:02 BoxyUwU

rebased. Are there any requested changes left? I think they're all addressed

jakobhellermann avatar May 28 '23 12:05 jakobhellermann

Looks like CI is failing due to #7905, which renamed the last_change_tick and change_tick fields to last_run and this_run. Also, I don't think the Complex tag is necessary since the only thing this function does is call a closure.

joseph-gio avatar May 28 '23 21:05 joseph-gio

@jakobhellermann once this is passing CI I'll merge it in :)

alice-i-cecile avatar May 29 '23 15:05 alice-i-cecile

Adding Adopt-Me on this since it's been a week <3 Shouldn't be hard to clean up at all.

alice-i-cecile avatar Jun 05 '23 20:06 alice-i-cecile

Closing in favor of #9194, which is an adoption of this PR :)

joseph-gio avatar Jul 21 '23 19:07 joseph-gio