-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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] - Implement and require #[derive(Component)]
on all component structs
#2254
Conversation
#1843 is tagged with |
crates/bevy_app/src/app_builder.rs
Outdated
@@ -252,7 +252,7 @@ impl AppBuilder { | |||
/// adding [State::get_driver] to additional stages you need it in. | |||
pub fn add_state<T>(&mut self, initial: T) -> &mut Self | |||
where | |||
T: Component + Debug + Clone + Eq + Hash, | |||
T: StateData, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this change a lot. Dramatically clearer and safer.
crates/bevy_app/src/app_builder.rs
Outdated
@@ -292,7 +292,7 @@ impl AppBuilder { | |||
/// and inserting a `Events::<T>::update_system` system into `CoreStage::First`. | |||
pub fn add_event<T>(&mut self) -> &mut Self | |||
where | |||
T: Component, | |||
T: Resource, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yay separating out these bounds! I'll need to swap this to T: Component + Resource
for #1626, but that shouldn't be any issue.
@@ -146,20 +143,20 @@ impl<'a> LoadContext<'a> { | |||
|
|||
/// The result of loading an asset of type `T` | |||
#[derive(Debug)] | |||
pub struct AssetResult<T: Component> { | |||
pub struct AssetResult<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This surprises me a bit: why were we able to remove this component bound / why did it exist in the first place?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure exactly. I was surprised about that bound as well, as it didn't make much sense to me. I think it bound should be more like T: Asset
, but clearly that bound wasn't used either. Removing it seems to be just fine, so I went with that.
@@ -13,7 +14,7 @@ use std::{ | |||
}; | |||
|
|||
/// A collection of labels | |||
#[derive(Default, Reflect)] | |||
#[derive(Component, Default, Reflect)] | |||
#[reflect(Component)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any chance we can wrap this into the derive, probably in a follow-up PR? That would make working with reflection much less frustrating.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I believe this is one of the first things we might add. But I intentionally kept it as is to limit the scope of changes here. Details around automatic component reflection might need further discussion.
I believe there was a green light for this minimal implementation. Though it might have ben said only on discord. I'm explicilty avoiding adding any more features than necessary here, as those might indeed need further discussion and possible RFC. This PR doesn't close that linked issue. |
crates/bevy_ecs/src/component/mod.rs
Outdated
// For our own convinience, let's implement Component for primitives in tests. | ||
// It will eventually be removed, once tests are not using them anymore. | ||
// Consider those impls deprecated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we create an issue for this (if/when) this PR gets merged?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, it's a good follow-up once those changes are accepted. Unless we decide to get rid of that in this PR anyway :)
crates/bevy_ecs/src/component/mod.rs
Outdated
@@ -13,7 +14,7 @@ use thiserror::Error; | |||
/// A component is data associated with an [`Entity`](crate::entity::Entity). Each entity can have | |||
/// multiple different types of components, but only one of them per type. | |||
/// | |||
/// Any type that is `Send + Sync + 'static` automatically implements `Component`. | |||
/// Any type that is `Send + Sync + 'static` can implement `Component` using `#[derive(Component)]`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like a comment here describing how to work around the orphan rules for new Rust users.
Another follow-up issue that should be created for discussion: require |
Hmm I'm not remembering that (but my memory isn't perfect 😄). I am pretty sold on this, but my stance is still this: #1843 (comment). Before merging I'd like an RFC with a clear plan for the future (I'm most interested in what the Component trait will look like and the derive macro interface), and some killer feature implemented to make this more palatable for users. As it stands from a user perspective, this pr currently just (1) adds boilerplate and (2) solves a problem that, while common, probably isn't on most people's radar. I think implementing static storage should be that killer feature, because it:
|
Okay, I'm fine with that. The reason I wanted the minimal version in the first place is due to current efforts to bring relations into ECS. Implementing type-level storages will require deeper changes that would severely conflict with that branch. So, we have a choice to make here. I can ignore that conflict and start the implementation anyway. This would allow us to measure performance gains quickly, but later will add nontrivial merging overhead. Alternatively, this can just wait until relations are merged in. |
I would prefer to do this before relations; it may open up new architectural options for us to help resolve some of the outstanding concerns. |
This is a tough call. I think this will be less controversial and easier to review/merge than relations. But no matter what we do somebody is gonna need to deal with a->b or b->a merge conflicts (likely either me, you, or @BoxyUwU). I personally think moving each project forward independently and merging them when ready is the right call. Merge conflicts aren't fun, but I don't think they should be a show stopper here. |
I've added very basic implementation for static storages. Now derive understands Some benchmark numbers:
This implementation is actually fairly straightforward and doesn't introduce any hard to resolve conflicts. In the future, we could do more in-depth refactoring to simplify the iterator implementations and deduplicate some code. |
bors r+ |
…#2254) This implements the most minimal variant of #1843 - a derive for marker trait. This is a prerequisite to more complicated features like statically defined storage type or opt-out component reflection. In order to make component struct's purpose explicit and avoid misuse, it must be annotated with `#[derive(Component)]` (manual impl is discouraged for compatibility). Right now this is just a marker trait, but in the future it might be expanded. Making this change early allows us to make further changes later without breaking backward compatibility for derive macro users. This already prevents a lot of issues, like using bundles in `insert` calls. Primitive types are no longer valid components as well. This can be easily worked around by adding newtype wrappers and deriving `Component` for them. One funny example of prevented bad code (from our own tests) is when an newtype struct or enum variant is used. Previously, it was possible to write `insert(Newtype)` instead of `insert(Newtype(value))`. That code compiled, because function pointers (in this case newtype struct constructor) implement `Send + Sync + 'static`, so we allowed them to be used as components. This is no longer the case and such invalid code will trigger a compile error. Co-authored-by: = <=> Co-authored-by: TheRawMeatball <[email protected]> Co-authored-by: Carter Anderson <[email protected]>
#[derive(Component)]
on all component structs#[derive(Component)]
on all component structs
# Objective - Bevy has several `compile_fail` test - #2254 added `#[derive(Component)]` - Those tests now fail for a different reason. - This was not caught as these test still "successfully" failed to compile. ## Solution - Add `#[derive(Component)]` to the doctest - Also changed their cfg attribute from `doc` to `doctest`, so that these tests don't appear when running `cargo doc` with `--document-private-items`
*This PR description is an edited copy of #5007, written by @alice-i-cecile.* # Objective Follow-up to #2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds. While ergonomic, this results in several drawbacks: * it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource * it is challenging to discover if a type is intended to be used as a resource * we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component). * dependencies can use the same Rust type as a resource in invisibly conflicting ways * raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values * we cannot capture a definitive list of possible resources to display to users in an editor ## Notes to reviewers * Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits. *ira: My commits are not as well organized :')* * I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does. * I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with #4981. ## Changelog `Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro. ## Migration Guide Add `#[derive(Resource)]` to all types you are using as a resource. If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics. `ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing. Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead. Co-authored-by: Alice <[email protected]> Co-authored-by: Alice Cecile <[email protected]> Co-authored-by: devil-ira <[email protected]> Co-authored-by: Carter Anderson <[email protected]>
…yengine#5577) *This PR description is an edited copy of bevyengine#5007, written by @alice-i-cecile.* # Objective Follow-up to bevyengine#2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds. While ergonomic, this results in several drawbacks: * it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource * it is challenging to discover if a type is intended to be used as a resource * we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component). * dependencies can use the same Rust type as a resource in invisibly conflicting ways * raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values * we cannot capture a definitive list of possible resources to display to users in an editor ## Notes to reviewers * Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits. *ira: My commits are not as well organized :')* * I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does. * I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with bevyengine#4981. ## Changelog `Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro. ## Migration Guide Add `#[derive(Resource)]` to all types you are using as a resource. If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics. `ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing. Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead. Co-authored-by: Alice <[email protected]> Co-authored-by: Alice Cecile <[email protected]> Co-authored-by: devil-ira <[email protected]> Co-authored-by: Carter Anderson <[email protected]>
…yengine#5577) *This PR description is an edited copy of bevyengine#5007, written by @alice-i-cecile.* # Objective Follow-up to bevyengine#2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds. While ergonomic, this results in several drawbacks: * it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource * it is challenging to discover if a type is intended to be used as a resource * we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component). * dependencies can use the same Rust type as a resource in invisibly conflicting ways * raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values * we cannot capture a definitive list of possible resources to display to users in an editor ## Notes to reviewers * Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits. *ira: My commits are not as well organized :')* * I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does. * I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with bevyengine#4981. ## Changelog `Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro. ## Migration Guide Add `#[derive(Resource)]` to all types you are using as a resource. If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics. `ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing. Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead. Co-authored-by: Alice <[email protected]> Co-authored-by: Alice Cecile <[email protected]> Co-authored-by: devil-ira <[email protected]> Co-authored-by: Carter Anderson <[email protected]>
…yengine#5577) *This PR description is an edited copy of bevyengine#5007, written by @alice-i-cecile.* # Objective Follow-up to bevyengine#2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds. While ergonomic, this results in several drawbacks: * it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource * it is challenging to discover if a type is intended to be used as a resource * we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component). * dependencies can use the same Rust type as a resource in invisibly conflicting ways * raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values * we cannot capture a definitive list of possible resources to display to users in an editor ## Notes to reviewers * Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits. *ira: My commits are not as well organized :')* * I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does. * I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with bevyengine#4981. ## Changelog `Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro. ## Migration Guide Add `#[derive(Resource)]` to all types you are using as a resource. If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics. `ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing. Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead. Co-authored-by: Alice <[email protected]> Co-authored-by: Alice Cecile <[email protected]> Co-authored-by: devil-ira <[email protected]> Co-authored-by: Carter Anderson <[email protected]>
*This PR description is an edited copy of #5007, written by @alice-i-cecile.* # Objective Follow-up to bevyengine/bevy#2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds. While ergonomic, this results in several drawbacks: * it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource * it is challenging to discover if a type is intended to be used as a resource * we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component). * dependencies can use the same Rust type as a resource in invisibly conflicting ways * raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values * we cannot capture a definitive list of possible resources to display to users in an editor ## Notes to reviewers * Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits. *ira: My commits are not as well organized :')* * I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does. * I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with bevyengine/bevy#4981. ## Changelog `Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro. ## Migration Guide Add `#[derive(Resource)]` to all types you are using as a resource. If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics. `ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing. Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead. Co-authored-by: Alice <[email protected]> Co-authored-by: Alice Cecile <[email protected]> Co-authored-by: devil-ira <[email protected]> Co-authored-by: Carter Anderson <[email protected]>
This implements the most minimal variant of #1843 - a derive for marker trait. This is a prerequisite to more complicated features like statically defined storage type or opt-out component reflection.
In order to make component struct's purpose explicit and avoid misuse, it must be annotated with
#[derive(Component)]
(manual impl is discouraged for compatibility). Right now this is just a marker trait, but in the future it might be expanded. Making this change early allows us to make further changes later without breaking backward compatibility for derive macro users.This already prevents a lot of issues, like using bundles in
insert
calls. Primitive types are no longer valid components as well. This can be easily worked around by adding newtype wrappers and derivingComponent
for them.One funny example of prevented bad code (from our own tests) is when an newtype struct or enum variant is used. Previously, it was possible to write
insert(Newtype)
instead ofinsert(Newtype(value))
. That code compiled, because function pointers (in this case newtype struct constructor) implementSend + Sync + 'static
, so we allowed them to be used as components. This is no longer the case and such invalid code will trigger a compile error.