bevy/crates/bevy_ecs/examples/derive_label.rs
JoJoJet 44e9cd4bfc Add attribute to ignore fields of derived labels (#5366)
# Objective

Fixes #5362 

## Solution

Add the attribute `#[label(ignore_fields)]` for `*Label` types.

```rust
#[derive(SystemLabel)]
pub enum MyLabel {
    One,

    // Previously this was not allowed since labels cannot contain data.
    #[system_label(ignore_fields)]
    Two(PhantomData<usize>),
}
```

## Notes

This label makes it possible for equality to behave differently depending on whether or not you are treating the type as a label. For example:

```rust
#[derive(SystemLabel, PartialEq, Eq)]
#[system_label(ignore_fields)]
pub struct Foo(usize);
```

If you compare it as a label, it will ignore the wrapped fields as the user requested. But if you compare it as a `Foo`, the derive will incorrectly compare the inner fields. I see a few solutions

1. Do nothing. This is technically intended behavior, but I think we should do our best to prevent footguns.
2. Generate impls of `PartialEq` and `Eq` along with the `#[derive(Label)]` macros. This is a breaking change as it requires all users to remove these derives from their types.
3. Only allow `PhantomData` to be used with `ignore_fields` -- seems needlessly prescriptive.

---

## Changelog

* Added the `ignore_fields` attribute to the derive macros for `*Label` types.
* Added an example showing off different forms of the derive macro.

<!--
## Migration Guide

> This section is optional. If there are no breaking changes, you can delete this section.

- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.
-->
2022-07-19 05:21:19 +00:00

62 lines
1.3 KiB
Rust

use std::marker::PhantomData;
use bevy_ecs::prelude::*;
fn main() {
// Unit labels are always equal.
assert_eq!(UnitLabel.as_label(), UnitLabel.as_label());
// Enum labels depend on the variant.
assert_eq!(EnumLabel::One.as_label(), EnumLabel::One.as_label());
assert_ne!(EnumLabel::One.as_label(), EnumLabel::Two.as_label());
// Labels annotated with `ignore_fields` ignore their fields.
assert_eq!(WeirdLabel(1).as_label(), WeirdLabel(2).as_label());
// Labels don't depend only on the variant name but on the full type
assert_ne!(
GenericLabel::<f64>::One.as_label(),
GenericLabel::<char>::One.as_label(),
);
}
#[derive(SystemLabel)]
pub struct UnitLabel;
#[derive(SystemLabel)]
pub enum EnumLabel {
One,
Two,
}
#[derive(SystemLabel)]
#[system_label(ignore_fields)]
pub struct WeirdLabel(i32);
#[derive(SystemLabel)]
pub enum GenericLabel<T> {
One,
#[system_label(ignore_fields)]
Two(PhantomData<T>),
}
// FIXME: this should be a compile_fail test
/*#[derive(SystemLabel)]
pub union Foo {
x: i32,
}*/
// FIXME: this should be a compile_fail test
/*#[derive(SystemLabel)]
#[system_label(ignore_fields)]
pub enum BadLabel {
One,
Two,
}*/
// FIXME: this should be a compile_fail test
/*#[derive(SystemLabel)]
pub struct BadLabel2 {
#[system_label(ignore_fields)]
x: (),
}*/