Required components accept const values (#16720)#18309
Required components accept const values (#16720)#18309alice-i-cecile merged 5 commits intobevyengine:mainfrom
Conversation
| } | ||
| } else if input.peek(Token![=]) { | ||
| let _t: syn::Token![=] = input.parse()?; | ||
| let label: Ident = input.parse()?; |
There was a problem hiding this comment.
We could parse out an expression instead and support more ways to set the value.
There was a problem hiding this comment.
yes, but you would somehow need to extract the type from the expression, so it will be messy
Carter0
left a comment
There was a problem hiding this comment.
Not really a macro expert but this seems reasonable. I think it would be really awesome if we had a required components example showcasing this work, but I can do that in another PR too
alice-i-cecile
left a comment
There was a problem hiding this comment.
A bit implicit but I think it's worth it. Using constants here should be dead easy.
# Objective
Const values should be more ergonomic to insert, since this is too
verbose
``` rust
#[derive(Component)]
#[require(
LockedAxes(||LockedAxes::ROTATION_LOCKED),
)]
pub struct CharacterController;
```
instead, users can now abbreviate that nonsense like this
``` rust
#[derive(Component)]
#[require(
LockedAxes = ROTATION_LOCKED),
)]
pub struct CharacterController;
```
it also works for enum labels.
I chose to omit the type, since were trying to reduce typing here. The
alternative would have been this:
```rust
#[require(
LockedAxes = LockedAxes::ROTATION_LOCKED),
)]
```
This of course has its disadvantages, since the const has to be
associated, but the old closure method is still possible, so I dont
think its a problem.
- Fixes #16720
## Testing
I added one new test in the docs, which also explain the new change. I
also saw that the docs for the required components on line 165 was
missing an assertion, so I added it back in
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
|
I would have preferred the less implicit: #[derive(Component)]
#[require(
LockedAxes = LockedAxes::ROTATION_LOCKED),
)]
pub struct CharacterController;That would also support this pattern: const CHARACTER_CONTROLLER_LOCKED_AXES: LockedAxes = LockedAxes::from_bits(...);
#[derive(Component)]
#[require(
LockedAxes = CHARACTER_CONTROLLER_LOCKED_AXES,
)]
pub struct CharacterController;The current implementation is unintuitive and will be confusingly broken when people try to do something like the above example. |
|
Actually I think we should go even further and do this: require(
Transform, // becomes Transform::default
LockedAxes::ROTATION_LOCKED, // becomes || LockedAxes::ROTATION_LOCKED
Health(10), // becomes || Health(10)
Defense = || Defense(8), // becomes || Defense(8)
Speed = my_speed, // becomes my_speed
Position { x: 10, y: 20 }, // becomes || Position { x: 10, y: 20 }
)This would be a breaking change, but I think its what pretty much everyone actually wants. I'll take a stab at this |
# Objective Requires are currently more verbose than they need to be. People would like to define inline component values. Additionally, the current `#[require(Foo(custom_constructor))]` and `#[require(Foo(|| Foo(10))]` syntax doesn't really make sense within the context of the Rust type system. #18309 was an attempt to improve ergonomics for some cases, but it came at the cost of even more weirdness / unintuitive behavior. Our approach as a whole needs a rethink. ## Solution Rework the `#[require()]` syntax to make more sense. This is a breaking change, but I think it will make the system easier to learn, while also improving ergonomics substantially: ```rust #[derive(Component)] #[require( A, // this will use A::default() B(1), // inline tuple-struct value C { value: 1 }, // inline named-struct value D::Variant, // inline enum variant E::SOME_CONST, // inline associated const F::new(1), // inline constructor G = returns_g(), // an expression that returns G H = SomethingElse::new(), // expression returns SomethingElse, where SomethingElse: Into<H> )] struct Foo; ``` ## Migration Guide Custom-constructor requires should use the new expression-style syntax: ```rust // before #[derive(Component)] #[require(A(returns_a))] struct Foo; // after #[derive(Component)] #[require(A = returns_a())] struct Foo; ``` Inline-closure-constructor requires should use the inline value syntax where possible: ```rust // before #[derive(Component)] #[require(A(|| A(10))] struct Foo; // after #[derive(Component)] #[require(A(10)] struct Foo; ``` In cases where that is not possible, use the expression-style syntax: ```rust // before #[derive(Component)] #[require(A(|| A(10))] struct Foo; // after #[derive(Component)] #[require(A = A(10)] struct Foo; ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François Mockers <mockersf@gmail.com>
# Objective Requires are currently more verbose than they need to be. People would like to define inline component values. Additionally, the current `#[require(Foo(custom_constructor))]` and `#[require(Foo(|| Foo(10))]` syntax doesn't really make sense within the context of the Rust type system. #18309 was an attempt to improve ergonomics for some cases, but it came at the cost of even more weirdness / unintuitive behavior. Our approach as a whole needs a rethink. ## Solution Rework the `#[require()]` syntax to make more sense. This is a breaking change, but I think it will make the system easier to learn, while also improving ergonomics substantially: ```rust #[derive(Component)] #[require( A, // this will use A::default() B(1), // inline tuple-struct value C { value: 1 }, // inline named-struct value D::Variant, // inline enum variant E::SOME_CONST, // inline associated const F::new(1), // inline constructor G = returns_g(), // an expression that returns G H = SomethingElse::new(), // expression returns SomethingElse, where SomethingElse: Into<H> )] struct Foo; ``` ## Migration Guide Custom-constructor requires should use the new expression-style syntax: ```rust // before #[derive(Component)] #[require(A(returns_a))] struct Foo; // after #[derive(Component)] #[require(A = returns_a())] struct Foo; ``` Inline-closure-constructor requires should use the inline value syntax where possible: ```rust // before #[derive(Component)] #[require(A(|| A(10))] struct Foo; // after #[derive(Component)] #[require(A(10)] struct Foo; ``` In cases where that is not possible, use the expression-style syntax: ```rust // before #[derive(Component)] #[require(A(|| A(10))] struct Foo; // after #[derive(Component)] #[require(A = A(10)] struct Foo; ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François Mockers <mockersf@gmail.com>
Objective
Const values should be more ergonomic to insert, since this is too verbose
instead, users can now abbreviate that nonsense like this
it also works for enum labels.
I chose to omit the type, since were trying to reduce typing here. The alternative would have been this:
This of course has its disadvantages, since the const has to be associated, but the old closure method is still possible, so I dont think its a problem.
Testing
I added one new test in the docs, which also explain the new change. I also saw that the docs for the required components on line 165 was missing an assertion, so I added it back in