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

add MutUntyped::map_unchanged #6430

Closed

Conversation

jakobhellermann
Copy link
Contributor

@jakobhellermann jakobhellermann commented Oct 31, 2022

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 simple 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
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
Copy link
Member

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

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.

crates/bevy_ecs/src/change_detection.rs Outdated Show resolved Hide resolved
crates/bevy_ecs/src/change_detection.rs Show resolved Hide resolved
Copy link
Member

@JoJoJet JoJoJet left a comment

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.

crates/bevy_ecs/src/change_detection.rs Outdated Show resolved Hide resolved
Copy link
Member

@JoJoJet JoJoJet left a comment

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.

crates/bevy_ecs/src/change_detection.rs Outdated Show resolved Hide resolved
@alice-i-cecile
Copy link
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
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
Member

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

@jakobhellermann
Copy link
Contributor Author

CI is green

crates/bevy_ecs/src/change_detection.rs Outdated Show resolved Hide resolved
@alice-i-cecile alice-i-cecile added this to the 0.10 milestone Feb 17, 2023
@alice-i-cecile
Copy link
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
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
Copy link
Contributor

bors bot commented Feb 17, 2023

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
Copy link
Contributor

bors bot commented Feb 17, 2023

Build failed:

@BoxyUwU
Copy link
Member

BoxyUwU commented Feb 26, 2023

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

@jakobhellermann
Copy link
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
Copy link
Member

BoxyUwU commented Feb 26, 2023

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

@jakobhellermann
Copy link
Contributor Author

jakobhellermann commented May 28, 2023

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

Copy link
Member

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

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
@JoJoJet JoJoJet removed the D-Complex Quite challenging from either a design or technical perspective. Ask for help! label May 28, 2023
@JoJoJet
Copy link
Member

JoJoJet commented May 28, 2023

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
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
Member

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

@JoJoJet
Copy link
Member

JoJoJet commented Jul 21, 2023

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

@JoJoJet JoJoJet 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 simple 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.

None yet

5 participants