-
-
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
External components with #[derive(Component)] #2966
Comments
I think reconsidering (1) is worth it, given that ecosystem compatibility wasn't a (major) part of the discussion in the RFC. I think we should weigh "ecosystem compatibility" / "everything is a component" vs the various reasons for adopting |
Fair enough; while I disagree with doing so, reversion is an option. The "impossible" part of 1 was specifically for implementing FYI, I don't think that reverting this change will actually solve the ecosystem problems. It only hides them in the simplest of cases.
This fact means that I actually think that we should go in the other direction, and unify things with |
There's actually a sixth option: we get negative trait bounds into This is probably my personal favorite on a lot of levels, but isn't happening for a good long while, no matter how much effort the Bevy community throws at the problem :( |
About (1) "Bevy implements We could setup a process where a library author can submit the components into Bevy. With an appropriate code owner file and directory structure, the author could "own" this part of the code even though its in Bevy repo |
From an organizational perspective, I'm not sure that this solves any problems that approach 2 doesn't. If the crate doesn't care about Bevy, they won't bother adding to this list. I guess you could have random unauthorized end users submitting this to Bevy, which gets around that. But that seems... very unpleasant and chaotic, and will cause huge issues if the crate owners themselves change their mind and want to integrate with Bevy in their own ways. Fundamentally, I think that the ownership rules are there for a good reason. |
If it's like alice is saying, I think 1 should be excluded from options, this restraint should be put in place as early as possible, as it will be much more difficult to change later. Meanwhile, it may be a good idea to bring more attention to the rust-lang delegate PR, problems like here are bound to happen elsewhere and delaying it only makes it worse. |
A variant on proposal 4, proposed by Create a Bevy-endorsed pub trait IntoComponent {
type Component = C;
type StorageType;
fn into(self) -> C;
}
impl<T> IntoComponent for T: Component {
type Component = Self;
const StorageType = T::Storage::STORAGE_TYPE;
fn into(self) -> Self { self }
}
pub struct External<T>(pub T);
impl<T> IntoComponent for External<T> {
type Component = T;
// THIS IS AN IMPORTANT CAVEAT, SEE BELOW
const StorageType = StorageType::Dynamic;
fn into(self) -> Self { self }
}
impl EntityMut {
// all similar methods which take a component by value would require wrapping external components in External(value). Alternatively, we could have
// methods like `insert_external(&mut self, value: T)` which move this boilerplate to the method signature.
pub fn insert<T: IntoComponent>(&mut self, value: T) -> &mut Self { /* ... */ }
}
// Could achieve the same effect without changing the current T: Component bounds, but just having separate impls for `&External<T>`, `ReadFetch<External<T>>`, etc.
impl<T: IntoComponent> WorldQuery for &T {
// contents remain unchanged (though bounds on ReadFetch are also altered to match, see below)
}
impl<'w, 's, T: IntoComponent> Fetch<'w, 's> for ReadFetch<T> {
type Item = &'w T::Component;
// basically the same
}
// ...the same kind of tweaked impls for &mut T, With<T>, Without<T>, etc. The caveat mentioned above is that In |
Removed from the 0.6 milestone because we have resolved to use |
Closing this out. The migration seems to have gone relatively smoothly. |
With the introduction of an opt-in
Component
trait in #2254, external types can no longer be directly included as components.In the standard scenario, we have 4 parties:
Due to Rust's orphan rules, only Bevy or the external crate can implement
Component
for arbitrary external crate structs directly. The interface crate or end user can only bypass this using a wrapper type, and using a technique known as extension traits.Users need to be able to use external types as components, in one form or another.
There are several options for this:
Component
for external types. This is impossible, as this set is unknowable, and using overly broad blanket impls causes all of the issues that [Merged by Bors] - Implement and require#[derive(Component)]
on all component structs #2254 solved.Component
for the relevant types, possibly hidden behind a feature flag. This is the solution used forserde
and other ecosystem crates. This is ideal, but not always going to happen, as it is outside of the control of anyone who cares about Bevy.ExternalComponent<T, StorageType>
struct and implements component on it. This avoids accidental conflicts and reduces boilerplate, but reduces the type safety of the API in some of the ways that [Merged by Bors] - Implement and require#[derive(Component)]
on all component structs #2254 solved.ExternalComponent<T, StorageType>
struct and implements component on it. This avoids ecosystem fragmentation, but has most of the same problems as 4. Extending this becomes increasingly onerous as we add more configuration to component types.Given that 1 is impossible, and 2 is not enforceable, we are left with a choice between 3-5.
The tentative consensus from the active ECS contributors is that 3 is the least bad option, with the encouragement to use
Deref
andDerefMut
on the wrapper types until delegation is one day (🤞🏽) added to Rust.4 is a particularly bad solution due to the impact on type safety and difficulty extending it. 5 is less bad than 4 in some ways, as getting 4 correct is going to be frustrating for new users and its easy to screw up and make a completely unconfigurable wrapper type. However, by including an
ExternalComponent
type in Bevy itself, we are effectively endorsing that solution.Thus we should:
Reflect
even without aResource
trait), credit to @BoxyUwUThe text was updated successfully, but these errors were encountered: