bevy/examples/ecs
Zachary Harrold ff1143ec87
Remove deprecated component_reads_and_writes (#16348)
# Objective

- Fixes #16339

## Solution

- Replaced `component_reads_and_writes` and `component_writes` with
`try_iter_component_access`.

## Testing

- Ran `dynamic` example to confirm behaviour is unchanged.
- CI

---

## Migration Guide

The following methods (some removed in previous PRs) are now replaced by
`Access::try_iter_component_access`:

* `Access::component_reads_and_writes`
* `Access::component_reads`
* `Access::component_writes`

As `try_iter_component_access` returns a `Result`, you'll now need to
handle the failing case (e.g., `unwrap()`). There is currently a single
failure mode, `UnboundedAccess`, which occurs when the `Access` is for
all `Components` _except_ certain exclusions. Since this list is
infinite, there is no meaningful way for `Access` to provide an
iterator. Instead, get a list of components (e.g., from the `Components`
structure) and iterate over that instead, filtering using
`Access::has_component_read`, `Access::has_component_write`, etc.

Additionally, you'll need to `filter_map` the accesses based on which
method you're attempting to replace:

* `Access::component_reads_and_writes` -> `Exclusive(_) | Shared(_)`
* `Access::component_reads` -> `Shared(_)`
* `Access::component_writes` -> `Exclusive(_)`

To ease migration, please consider the below extension trait which you
can include in your project:

```rust
pub trait AccessCompatibilityExt {
    /// Returns the indices of the components this has access to.
    fn component_reads_and_writes(&self) -> impl Iterator<Item = T> + '_;

    /// Returns the indices of the components this has non-exclusive access to.
    fn component_reads(&self) -> impl Iterator<Item = T> + '_;

    /// Returns the indices of the components this has exclusive access to.
    fn component_writes(&self) -> impl Iterator<Item = T> + '_;
}

impl<T: SparseSetIndex> AccessCompatibilityExt for Access<T> {
    fn component_reads_and_writes(&self) -> impl Iterator<Item = T> + '_ {
        self
            .try_iter_component_access()
            .expect("Access is unbounded. Please refactor the usage of this method to directly use try_iter_component_access")
            .filter_map(|component_access| {
                let index = component_access.index().sparse_set_index();

                match component_access {
                    ComponentAccessKind::Archetypal(_) => None,
                    ComponentAccessKind::Shared(_) => Some(index),
                    ComponentAccessKind::Exclusive(_) => Some(index),
                }
            })
    }

    fn component_reads(&self) -> impl Iterator<Item = T> + '_ {
        self
            .try_iter_component_access()
            .expect("Access is unbounded. Please refactor the usage of this method to directly use try_iter_component_access")
            .filter_map(|component_access| {
                let index = component_access.index().sparse_set_index();

                match component_access {
                    ComponentAccessKind::Archetypal(_) => None,
                    ComponentAccessKind::Shared(_) => Some(index),
                    ComponentAccessKind::Exclusive(_) => None,
                }
            })
    }

    fn component_writes(&self) -> impl Iterator<Item = T> + '_ {
        self
            .try_iter_component_access()
            .expect("Access is unbounded. Please refactor the usage of this method to directly use try_iter_component_access")
            .filter_map(|component_access| {
                let index = component_access.index().sparse_set_index();

                match component_access {
                    ComponentAccessKind::Archetypal(_) => None,
                    ComponentAccessKind::Shared(_) => None,
                    ComponentAccessKind::Exclusive(_) => Some(index),
                }
            })
    }
}
```

Please take note of the use of `expect(...)` in these methods. You
should consider using these as a starting point for a more appropriate
migration based on your specific needs.

## Notes

- This new method is fallible based on whether the `Access` is bounded
or unbounded (unbounded occurring with inverted component sets). If
bounded, will return an iterator of every item and its access level. I
believe this makes sense without exposing implementation details around
`Access`.
- The access level is defined by an `enum` `ComponentAccessKind<T>`,
either `Archetypical`, `Shared`, or `Exclusive`. As a convenience, this
`enum` has a method `index` to get the inner `T` value without a match
statement. It does add more code, but the API is clearer.
- Within `QueryBuilder` this new method simplifies several pieces of
logic without changing behaviour.
- Within `QueryState` the logic is simplified and the amount of
iteration is reduced, potentially improving performance.
- Within the `dynamic` example it has identical behaviour, with the
inversion footgun explicitly highlighted by an `unwrap`.

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
Co-authored-by: Mike <2180432+hymm@users.noreply.github.com>
2025-03-04 08:22:29 +00:00
..
change_detection.rs Rename track_change_detection flag to track_location (#17075) 2025-01-01 18:43:47 +00:00
component_hooks.rs Upgrade to Rust Edition 2024 (#17967) 2025-02-24 03:54:47 +00:00
custom_query_param.rs Prefer Display over Debug (#16112) 2024-12-27 00:40:06 +00:00
custom_schedule.rs Simpler lint fixes: makes ci lints work but disables a lint for now (#15376) 2024-09-24 11:42:59 +00:00
dynamic.rs Remove deprecated component_reads_and_writes (#16348) 2025-03-04 08:22:29 +00:00
ecs_guide.rs Renamed EventWriter::send methods to write. (#17977) 2025-02-23 21:18:52 +00:00
entity_disabling.rs Make Query::single (and friends) return a Result (#18082) 2025-03-02 19:51:56 +00:00
event.rs Renamed EventWriter::send methods to write. (#17977) 2025-02-23 21:18:52 +00:00
fallible_params.rs Renamed members of ParamWarnPolicy to reflect new behaviour. (#17311) 2025-01-12 05:40:04 +00:00
fallible_systems.rs Bump typos to 1.30.0 (#18097) 2025-03-02 19:02:23 +00:00
fixed_timestep.rs aligning public apis of Time,Timer and Stopwatch (#15962) 2024-10-16 21:09:32 +00:00
generic_system.rs Relationships (non-fragmenting, one-to-many) (#17398) 2025-01-18 22:20:30 +00:00
hierarchy.rs Add an example teaching users about custom relationships (#17443) 2025-01-20 23:17:38 +00:00
immutable_components.rs Improved Entity Mapping and Cloning (#17687) 2025-02-06 22:13:41 +00:00
iter_combinations.rs aligning public apis of Time,Timer and Stopwatch (#15962) 2024-10-16 21:09:32 +00:00
nondeterministic_system_order.rs
observer_propagation.rs Renamed EventWriter::send methods to write. (#17977) 2025-02-23 21:18:52 +00:00
observers.rs Make Query::single (and friends) return a Result (#18082) 2025-03-02 19:51:56 +00:00
one_shot_systems.rs Merge Style properties into Node. Use ComputedNode for computed properties. (#15975) 2024-10-18 22:25:33 +00:00
parallel_query.rs Make Query::single (and friends) return a Result (#18082) 2025-03-02 19:51:56 +00:00
relationships.rs Add an example teaching users about custom relationships (#17443) 2025-01-20 23:17:38 +00:00
removal_detection.rs Rename trigger.entity() to trigger.target() (#16716) 2024-12-08 21:55:09 +00:00
run_conditions.rs aligning public apis of Time,Timer and Stopwatch (#15962) 2024-10-16 21:09:32 +00:00
send_and_receive_events.rs Renamed EventWriter::send methods to write. (#17977) 2025-02-23 21:18:52 +00:00
startup_system.rs
system_closure.rs Prefer Display over Debug (#16112) 2024-12-27 00:40:06 +00:00
system_param.rs Inverse missing_docs logic (#11676) 2024-02-03 21:40:55 +00:00
system_piping.rs Fix a few typos (#17292) 2025-01-10 22:48:30 +00:00
system_stepping.rs Remove stepping from default features (#12847) 2024-04-03 19:16:02 +00:00