-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Typed SystemLabels regress generic labeling #1497
Comments
For what it's worth, you can derive the label traits on structs or continue to use |
I tried to do so here: #1498 (comment) |
@cart I'd like to see if we can get a fix for this in for 0.5 to avoid a regression, especially for plugin authors. Would you be okay with adding the 0.5 milestone to this? |
This doesn't sound like a regression, given that SystemLabel is implemented for static strings and Cow strings (as @Ratysz mentioned). @aevyrie it looks like you tried creating a custom generic type and deriving SystemLabel for it (which you eventually got working). If you use strings (like you previously did in Supporting generics is still a good idea and seems worth adding to the milestone, but I wouldn't call this a regression. Its a "missing feature" that we've never had before. |
Sorry if I'm being obtuse here. By "regression" I mean that I was previously able to make labels generic over components (just like systems), but I am now unable to do so. I'm using this in a bounding volume plugin so my systems can be generic over different types of bounding volume, which requires generic labels to solve system order ambiguity.
Does anyone have an example of a generic system with generic labels? That's all I really need, and I can't seem to find anyone else doing this (or the source doing this) for some reason. |
First: sorry for saying Your old approach should still work like this: .label(Cow::Owned(format!("update_boundvols_{}", std::any::type_name::<T>()))), The Cow::Owned is necessary because you weren't actually using static strings in your old impl. |
Haha, no worries. 😁
Ah, fantastic, well now I feel silly. This was a gap in my understanding of rust. This is definitely something worth documenting alongside the generic systems pattern. This generic label + generic systems is a really useful abstraction when you have many components that implement the same trait. I wonder if it's worth making a I'll attempt the suggested fix, document, and close if it works. Thanks! |
The suggested fix worked exactly as suggested. Closing. |
This comment has been minimized.
This comment has been minimized.
Ah, beat me by a couple of seconds. Although I would argue that putting bounds on EDIT: Actually, no, your snippet does not compile. |
The main thing that prevented me from moving away from stringly typing this was that this required all sorts of trait impls on my generic type. This is painful because my generics are components that often have fields which do not implement Hash or Eq. |
Hmm, this is a serious usability issue IMO. With the last change in #1498, the theoretical correct solution would be just #[derive(Debug, Hash, Eq, PartialEq, Copy, Clone, SystemLabel)]
enum Systems<T> {
Sys1,
Sys2,
_Marker(std::marker::PhantomData<T>),
} However, this does not work in practice because the built-in derives are too restrictive, and place excessive bounds on One possible solution would be to have bevy-specific macros that generate more precise impls at the cost of leaking the internals. |
I know this is unsatisfying, but to address usability, what do you think of making a |
This is something best left to user code. |
Can I get some more opinions on this? In such a world, we'd have derive macros like #[derive(LeakyDebug, LeakyHash, LeakyEq, LeakyPartialEq, LeakyCopy, LeakyClone, SystemLabel)]
enum Systems<T> {
Sys1,
Sys2,
_Marker(PhantomData<T>),
}
// This is the the type of code that gets generated:
impl Debug for Systems<T> where PhantomData<T>: Debug {}
impl Hash for Systems<T> where PhantomData<T>: Hash {}
// and so on |
I'll give you mine again anyway 😄 This feels like a lot of complexity on Bevy side to fix something that isn't Bevy's fault in a way that isn't Bevy-specific. |
I agree. I will again reiterate that strings solve the problem of generic labels in bevy, until upstream Rust limitations are solved. I think a macro to help prevent global collisions would be sufficient.
Assuming this was in response to my suggestion of a In fact, I think I'd prefer that stringly-typed labels be wrapped in a Bevy type, so we can enforce some rules about how label strings are constructed. |
Hmm, alright. Since there doesn't seem to be a crate with these macros, I guess I'll make mine and then link it in this issue. For now I'm closing this issue |
I have a feeling that most people using generics in their labels just want a type to uniquely identify their system. A workaround we could consider is to provide a built in #[derive(SystemLabel)]
pub struct Label<T>(PhantomData<T>);
impl<T> Default for Label<T> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<T> Clone for Label<T> {
fn clone(&self) -> Self {
Self(PhantomData)
}
}
impl<T> PartialEq for Label<T> {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl<T> fmt::Debug for Label<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Label<{}>", std::any::type_name::<T>())
}
}
impl<T> std::hash::Hash for Label<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
impl<T> Eq for Label<T> {} |
Fixes #1497 Co-authored-by: Carter Anderson <mcanders1@gmail.com>
With generic systems, often you will have a bunch of systems that share the same generic type. With this proposal, their labels would be identical, which is not desired because you want to specify order between system of the same type. However, I think the many-to-many labels @alice-i-cecile mentioned would solve that, in which case the generic is completely ignored and a system that is generic has the same label for all concrete types it is generic over. That's not to say a |
The crux of the issue is that derives on types containing That would allow labels like this to Just Work: #[derive(Debug, Hash, Eq, PartialEq, Copy, Clone, SystemLabel)]
enum Systems<T> {
Sys1,
Sys2,
_Marker(BetterPhantomData<T>),
} Implementation-wise, |
Yeah, that's right. The derives were what I struggled with when trying to use generic labels with a |
Bevy version
bevy\main
Operating system & version
Win10
What you did
Updating to main branch with #1473 disallows creating labels that are generic over a type.
What you expected to happen
When stringly typed, I could have labels that were unique per concrete type over a generic. I haven't found a workaround to do the same with typed labels in their current form. I'd like to have generic labels so that users of my plugin can have multiple concrete types of my generic system active at the same time.
What actually happened
Attempting to derive SystemLabel for an enum with generic type annotations results in an error, probably because the derive macro doesn't support generics in the signature. Using an enum without generics results in a runtime error due to a non-unique SystemLabel.
The text was updated successfully, but these errors were encountered: