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

[Merged by Bors] - Add a method for mapping Mut<T> -> Mut<U> #6199

Closed
wants to merge 10 commits into from

Conversation

JoJoJet
Copy link
Member

@JoJoJet JoJoJet commented Oct 8, 2022

Objective

When designing an API, you may wish to provide access only to a specific field of a component or resource. The current options for doing this in safe code are

  • *Mut::into_inner, which flags a change no matter what.
  • *Mut::bypass_change_detection, which misses all changes.

Solution

Add the method map_unchanged.

Example

// When run, zeroes the translation of every entity.
fn reset_all(mut transforms: Query<&mut Transform>) {
    for transform in &mut transforms {
        // We pinky promise not to modify `t` within the closure.
        let translation = transform.map_unchanged(|t| &mut t.translation);
        // Only reset the translation if it isn't already zero.
        translation.set_if_not_equal(Vec2::ZERO);
    }
}

Changelog

  • Added the method map_unchanged to types Mut<T>, ResMut<T>, and NonSendMut<T>.

@inodentry
Copy link
Contributor

I don't quite understand why this is necessary? What are you planning to use it for?

@maniwani
Copy link
Contributor

maniwani commented Oct 8, 2022

I think #4413 is related.

@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use labels Oct 8, 2022
@alice-i-cecile
Copy link
Member

Clever. If we can improve the docs 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
@JoJoJet JoJoJet requested a review from alice-i-cecile October 8, 2022 21:12
@maniwani
Copy link
Contributor

maniwani commented Oct 8, 2022

Should this method be unsafe? There's no memory safety issue, but the "caller pinky promises not to mutate" part seems like it shares the sentiment. (Edit: Like we're asking the user to uphold our "change detection is reliable" invariant, but I guess bypass_change_detection isn't marked as unsafe either.)

@JoJoJet
Copy link
Member Author

JoJoJet commented Oct 8, 2022

Should this method be unsafe?

I don't think there's any need for it to be unsafe, but I'm willing to change it if the consensus disagrees with me.

@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 Oct 8, 2022
@mockersf
Copy link
Member

mockersf commented Oct 9, 2022

This is not unsafe, it's "if you do dumb things, dumb things will happen". Until we can declare our own function colours in Rust, we can't really mark those 🤷

@mockersf
Copy link
Member

mockersf commented Oct 9, 2022

@TheRawMeatball should this close #4413 if it's merged?

@alice-i-cecile
Copy link
Member

bors r+

bors bot pushed a commit that referenced this pull request Oct 10, 2022
# Objective

When designing an API, you may wish to provide access only to a specific field of a component or resource. The current options for doing this in safe code are

* `*Mut::into_inner`, which flags a change no matter what.
* `*Mut::bypass_change_detection`, which misses all changes.

## Solution

Add the method `map_unchanged`.

### Example

```rust
// When run, zeroes the translation of every entity.
fn reset_all(mut transforms: Query<&mut Transform>) {
    for transform in &mut transforms {
        // We pinky promise not to modify `t` within the closure.
        let translation = transform.map_unchanged(|t| &mut t.translation);
        // Only reset the translation if it isn't already zero.
        translation.set_if_not_equal(Vec2::ZERO);
    }
}
```

---

## Changelog

+ Added the method `map_unchanged` to types `Mut<T>`, `ResMut<T>`, and `NonSendMut<T>`.
@bors bors bot changed the title Add a method for mapping Mut<T> -> Mut<U> [Merged by Bors] - Add a method for mapping Mut<T> -> Mut<U> Oct 10, 2022
@bors bors bot closed this Oct 10, 2022
@JoJoJet JoJoJet deleted the map-mut branch October 10, 2022 17:33
james7132 pushed a commit to james7132/bevy that referenced this pull request Oct 19, 2022
# Objective

When designing an API, you may wish to provide access only to a specific field of a component or resource. The current options for doing this in safe code are

* `*Mut::into_inner`, which flags a change no matter what.
* `*Mut::bypass_change_detection`, which misses all changes.

## Solution

Add the method `map_unchanged`.

### Example

```rust
// When run, zeroes the translation of every entity.
fn reset_all(mut transforms: Query<&mut Transform>) {
    for transform in &mut transforms {
        // We pinky promise not to modify `t` within the closure.
        let translation = transform.map_unchanged(|t| &mut t.translation);
        // Only reset the translation if it isn't already zero.
        translation.set_if_not_equal(Vec2::ZERO);
    }
}
```

---

## Changelog

+ Added the method `map_unchanged` to types `Mut<T>`, `ResMut<T>`, and `NonSendMut<T>`.
james7132 pushed a commit to james7132/bevy that referenced this pull request Oct 28, 2022
# Objective

When designing an API, you may wish to provide access only to a specific field of a component or resource. The current options for doing this in safe code are

* `*Mut::into_inner`, which flags a change no matter what.
* `*Mut::bypass_change_detection`, which misses all changes.

## Solution

Add the method `map_unchanged`.

### Example

```rust
// When run, zeroes the translation of every entity.
fn reset_all(mut transforms: Query<&mut Transform>) {
    for transform in &mut transforms {
        // We pinky promise not to modify `t` within the closure.
        let translation = transform.map_unchanged(|t| &mut t.translation);
        // Only reset the translation if it isn't already zero.
        translation.set_if_not_equal(Vec2::ZERO);
    }
}
```

---

## Changelog

+ Added the method `map_unchanged` to types `Mut<T>`, `ResMut<T>`, and `NonSendMut<T>`.
Pietrek14 pushed a commit to Pietrek14/bevy that referenced this pull request Dec 17, 2022
# Objective

When designing an API, you may wish to provide access only to a specific field of a component or resource. The current options for doing this in safe code are

* `*Mut::into_inner`, which flags a change no matter what.
* `*Mut::bypass_change_detection`, which misses all changes.

## Solution

Add the method `map_unchanged`.

### Example

```rust
// When run, zeroes the translation of every entity.
fn reset_all(mut transforms: Query<&mut Transform>) {
    for transform in &mut transforms {
        // We pinky promise not to modify `t` within the closure.
        let translation = transform.map_unchanged(|t| &mut t.translation);
        // Only reset the translation if it isn't already zero.
        translation.set_if_not_equal(Vec2::ZERO);
    }
}
```

---

## Changelog

+ Added the method `map_unchanged` to types `Mut<T>`, `ResMut<T>`, and `NonSendMut<T>`.
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
# Objective

When designing an API, you may wish to provide access only to a specific field of a component or resource. The current options for doing this in safe code are

* `*Mut::into_inner`, which flags a change no matter what.
* `*Mut::bypass_change_detection`, which misses all changes.

## Solution

Add the method `map_unchanged`.

### Example

```rust
// When run, zeroes the translation of every entity.
fn reset_all(mut transforms: Query<&mut Transform>) {
    for transform in &mut transforms {
        // We pinky promise not to modify `t` within the closure.
        let translation = transform.map_unchanged(|t| &mut t.translation);
        // Only reset the translation if it isn't already zero.
        translation.set_if_not_equal(Vec2::ZERO);
    }
}
```

---

## Changelog

+ Added the method `map_unchanged` to types `Mut<T>`, `ResMut<T>`, and `NonSendMut<T>`.
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 C-Usability A targeted quality-of-life change that makes Bevy easier to use 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.

8 participants