Skip to content

add MutUntyped::map_unchanged#6430

Closed
jakobhellermann wants to merge 9 commits into
bevyengine:mainfrom
jakobhellermann:mutuntyped-to-typed
Closed

add MutUntyped::map_unchanged#6430
jakobhellermann wants to merge 9 commits into
bevyengine:mainfrom
jakobhellermann:mutuntyped-to-typed

Conversation

@jakobhellermann

@jakobhellermann jakobhellermann commented Oct 31, 2022

Copy link
Copy Markdown
Contributor

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 jakobhellermann added C-Usability A targeted quality-of-life change that makes Bevy easier to use A-Reflection Runtime information about types A-ECS Entities, components, systems, and events labels Oct 31, 2022
@alice-i-cecile

Copy link
Copy Markdown
Member

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 alice-i-cecile added the D-Complex Quite challenging from either a design or technical perspective. Ask for help! label Oct 31, 2022

@alice-i-cecile alice-i-cecile left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More pointer shenanigans. It makes sense why this is needed, and the docs are great. Very very grateful for the lifetimed pointer types here.

We can't check whether the types match, because we literally don't store that information on the MutUntyped :(

I'm on board.

Comment thread crates/bevy_ecs/src/change_detection.rs Outdated
Comment thread crates/bevy_ecs/src/change_detection.rs

@joseph-gio joseph-gio left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems very useful. This will probably simplify some internal code for trait queries.

Comment thread crates/bevy_ecs/src/change_detection.rs Outdated

@joseph-gio joseph-gio left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more things, but LGTM. The PR title and description need to be updated.

Comment thread crates/bevy_ecs/src/change_detection.rs Outdated
@alice-i-cecile

Copy link
Copy Markdown
Member

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

bors Bot pushed a commit that referenced this pull request Jan 11, 2023
# Objective

`MutUntyped` is a struct that stores a `PtrMut` alongside change tick metadata. Working with this type is cumbersome, and has few benefits over storing the pointer and change ticks separately.

Related: #6430 (title is out of date)

## Solution

Add a convenience method for transforming an untyped change detection pointer into its typed counterpart.

---

## Changelog

- Added the method `MutUntyped::with_type`.
alradish pushed a commit to alradish/bevy that referenced this pull request Jan 22, 2023
# Objective

`MutUntyped` is a struct that stores a `PtrMut` alongside change tick metadata. Working with this type is cumbersome, and has few benefits over storing the pointer and change ticks separately.

Related: bevyengine#6430 (title is out of date)

## Solution

Add a convenience method for transforming an untyped change detection pointer into its typed counterpart.

---

## Changelog

- Added the method `MutUntyped::with_type`.
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
# Objective

`MutUntyped` is a struct that stores a `PtrMut` alongside change tick metadata. Working with this type is cumbersome, and has few benefits over storing the pointer and change ticks separately.

Related: bevyengine#6430 (title is out of date)

## Solution

Add a convenience method for transforming an untyped change detection pointer into its typed counterpart.

---

## Changelog

- Added the method `MutUntyped::with_type`.
@jakobhellermann jakobhellermann changed the title add MutUntyped::to_typed add MutUntyped::map_unchanged Feb 13, 2023
@jakobhellermann

Copy link
Copy Markdown
Contributor Author

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

Done

@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Feb 13, 2023
@alice-i-cecile

Copy link
Copy Markdown
Member

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

@jakobhellermann

Copy link
Copy Markdown
Contributor Author

CI is green

Comment thread crates/bevy_ecs/src/change_detection.rs Outdated
Comment thread crates/bevy_ecs/src/change_detection.rs
@alice-i-cecile alice-i-cecile added this to the 0.10 milestone Feb 17, 2023
@alice-i-cecile

Copy link
Copy Markdown
Member

@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

Copy link
Copy Markdown
Member

bors r+

bors Bot pushed a commit that referenced this pull request Feb 17, 2023
# 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:
```rust
// 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
```rust
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
@bors

bors Bot commented Feb 17, 2023

Copy link
Copy Markdown

Build failed (retrying...):

bors Bot pushed a commit that referenced this pull request Feb 17, 2023
# 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:
```rust
// 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
```rust
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
@bors

bors Bot commented Feb 17, 2023

Copy link
Copy Markdown

Build failed:

@BoxyUwU

BoxyUwU commented Feb 26, 2023

Copy link
Copy Markdown
Member

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

@jakobhellermann

Copy link
Copy Markdown
Contributor Author

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.

@BoxyUwU

BoxyUwU commented Feb 26, 2023

Copy link
Copy Markdown
Member

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

Comment thread crates/bevy_ecs/src/change_detection.rs Outdated
@jakobhellermann

jakobhellermann commented May 28, 2023

Copy link
Copy Markdown
Contributor Author

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

@alice-i-cecile alice-i-cecile left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears complete now, and the PR description looks good.

@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label May 28, 2023
@joseph-gio joseph-gio removed the D-Complex Quite challenging from either a design or technical perspective. Ask for help! label May 28, 2023
@joseph-gio

Copy link
Copy Markdown
Member

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.

@alice-i-cecile

Copy link
Copy Markdown
Member

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

@alice-i-cecile alice-i-cecile added the S-Adopt-Me The original PR author has no intent to complete this work. Pick me up! label Jun 5, 2023
@alice-i-cecile

Copy link
Copy Markdown
Member

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

@joseph-gio

Copy link
Copy Markdown
Member

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

@joseph-gio joseph-gio closed this Jul 21, 2023
github-merge-queue Bot pushed a commit that referenced this pull request Jul 23, 2023
### **Adopted #6430**

# 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:


```rust
// 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

```rust
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

---------

Co-authored-by: Jakob Hellermann <jakob.hellermann@protonmail.com>
Co-authored-by: JoJoJet <21144246+JoJoJet@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ECS Entities, components, systems, and events A-Pointers Relating to Bevy pointer abstractions A-Reflection Runtime information about types C-Usability A targeted quality-of-life change that makes Bevy easier to use S-Adopt-Me The original PR author has no intent to complete this work. Pick me up! S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants