bevy icon indicating copy to clipboard operation
bevy copied to clipboard

Error when having 16 components with the new tuple .spawn(())

Open Dimonade opened this issue 2 years ago • 10 comments

Bevy version

bevy = {version = "0.9.1", features = ["dynamic", "wayland"]}

[Optional] Relevant system information

OS: Manjaro Linux x86_64 Kernel: 5.15.85-1-MANJARO

What you did

When creating 16 (different) components and using the new tuple way of spawning .spawn(()), building the project causes an error:

Components:

#[derive(Component)]
struct One;

#[derive(Component)]
struct Two;

#[derive(Component)]
struct Three;

#[derive(Component)]
struct Four;

#[derive(Component)]
struct Five;

#[derive(Component)]
struct Six;

#[derive(Component)]
struct Seven;

#[derive(Component)]
struct Eight;

#[derive(Component)]
struct Nine;

#[derive(Component)]
struct Ten;

#[derive(Component)]
struct Eleven;

#[derive(Component)]
struct Twelve;

#[derive(Component)]
struct Thirteen;

#[derive(Component)]
struct Fourteen;

#[derive(Component)]
struct Fifteen;

Spawning:

    commands.spawn((
        Name::new("Hi it's me"),
        One,
        Two,
        Three,
        Four,
        Five,
        Six,
        Seven,
        Eight,
        Nine,
        Ten,
        Eleven,
        Twelve,
        Thirteen,
        Fourteen,
        Fifteen
    ));

Error:

error[E0277]: the trait bound (bevy::prelude::Name, One, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Eleven, Twelve, Thirteen, Fourteen, Fifteen): bevy::prelude::Component is not satisfied

The components themselves do not matter, just that there are more than 15 of them:

    commands.spawn((
        Name::new("Hi it's me"),
        One,
        Two,
        Three,
        Four,
        Five,
        Six,
        Seven,
        Eight,
        Nine,
        Ten,
        Eleven,
        Twelve,
        // ...
        Fifteen,
        Sixteen,
        Seventeen,
    ));

Results in the same error.

However, when using the old way of spawning, .spawn().insert().insert()..., it works fine and compiles. It also compiles just fine when using one less component, i.e., just until Fourteen.

Old way:

    commands
        .spawn(Name::new("Hi it's me"))
        .insert(One)
        .insert(Two)
        .insert(Three)
        .insert(Four)
        .insert(Five)
        .insert(Six)
        .insert(Seven)
        .insert(Eight)
        .insert(Nine)
        .insert(Ten)
        .insert(Eleven)
        .insert(Twelve)
        .insert(Thirteen)
        .insert(Fourteen)
        .insert(Fifteen)
        .insert(Sixteen)
        .insert(Seventeen);

Works fine: 2023-01-22-125520_113x351_scrot

What went wrong

  • what were you expecting? All components to be inserted into the newly spawned object.

  • what actually happened? Only up to 16 components can be loaded using the new tuple way, after that it errors with:

error[E0277]: the trait bound `(bevy::prelude::Name, One, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Eleven, Twelve, Thirteen, Fourteen, Fifteen): bevy::prelude::Component` is not satisfied
   --> src/main.rs:355:20
    |
355 |       commands.spawn((
    |  ______________-----_^
    | |              |
    | |              required by a bound introduced by this call
356 | |         Name::new("Aye caramba"),
357 | |         One,
358 | |         Two,
...   |
371 | |         Fifteen
372 | |     ));
    | |_____^ the trait `bevy::prelude::Component` is not implemented for `(bevy::prelude::Name, One, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Eleven, Twelve, Thirteen, Fourteen, Fifteen)`
    |
    = help: the following other types implement trait `bevy::prelude::Component`:
              AdditionalMassProperties
              AlphaMode
              Analyzer
              AnimationPlayer
              AsyncCollider
              AsyncSceneCollider
              BackgroundColor
              Barracks
            and 191 others
    = note: required for `(bevy::prelude::Name, One, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Eleven, Twelve, Thirteen, Fourteen, Fifteen)` to implement `Bundle`

Additional information

The note in the message suggests aligning them into a Bundle, but if it is indeed the solution, then the limit of 16 with the new spawn system should be documented somewhere. However, the fact that there are no errors with the old way of spawning is a bit suspicious.

Shall any more info be required, please let me know.

Thanks for your help!

Dimonade avatar Jan 22 '23 13:01 Dimonade

Bundle is only implemented for tuples of up to 15 elements. however you can circumvent this by nesting the bundles e.g.

commands.spawn((
   (
        Name::new("Hi it's me"),
        One,
        Two,
        Three,
        Four,
        Five,
        Six,
        Seven,
        Eight,
    ),
    (
        Nine,
        Ten,
        Eleven,
        Twelve,
        Thirteen,
        Fourteen,
        Fifteen,
        Sixteen,
        Seventeen,
    ),
));

soqb avatar Jan 22 '23 13:01 soqb

Technically this limit is documented here. A lot of people have this confusion, though, so maybe it's not prominent enough? It could at least talk about the solution @soqb shared. Even though it encourages creating a custom struct for this, it's still good to know if really wanting to not create an additional type.

MrGVSV avatar Jan 22 '23 15:01 MrGVSV

Thanks all!

@soqb 's way is indeed a quick way to circumvent the issue that I am going to use for now. I think the main weakness point, is that with the old way one can add as many components without any limit (correct me if I'm wrong, in my example it worked with more than 16, but I didn't try to reach an upper limit), while the new way does have this limit. The docs do not mention this limit as far as I checked, so it might be a good idea to add it as another example (or a note, that there is a limit of 16, and one can either implement Bundle, or nest them together).

@MrGVSV : I think that creating a bundle might not be a good solution 100% of the time, because one might not be able to bind together components that might not have anything in common (if the project is large enough).

Dimonade avatar Jan 22 '23 17:01 Dimonade

I think the main weakness point, is that with the old way one can add as many components without any limit (correct me if I'm wrong, in my example it worked with more than 16, but I didn't try to reach an upper limit), while the new way does have this limit.

If by "old" way, you mean using insert, then that's still possible and doesn't have any limit (that I know of). Previously, spawn_bundle required an actual Bundle struct, which doesn't have the 15-max limitation.

The docs do not mention this limit as far as I checked, so it might be a good idea to add it as another example (or a note, that there is a limit of 16, and one can either implement Bundle, or nest them together).

Ah, I see what you mean. While Bundle does explicitly identify this limitation, the docs for Commands::spawn do not. I wonder if that needs to make note of the tuple limit as well or maybe just advise reading the Bundle docs for details/caveats.

@MrGVSV : I think that creating a bundle might not be a good solution 100% of the time, because that can bind together components that might not have anything in common (if the project is large enough).

Sure, but you don't necessarily need to export that type. Just define it locally to that module— or even to the system function— so that it "doesn't exist" as far as the rest of your project is concerned.

MrGVSV avatar Jan 22 '23 17:01 MrGVSV

@MrGVSV Yes, exactly, you're on point. In the Bundle it says that

Additionally, Tuples of bundles are also Bundle (with up to 15 bundles).

and

Tuple bundles can be nested, which can be used to create an anonymous bundle with more than 15 items.

In Commands::spawn however there is no mention of the limit or the number 15, and that would be the first place where I'd look for the issue, since the error also points to the .spawn(()) and mentions the Bundle on the very bottom of the text.

I think the easiest solution would be to add the note in the .spawn(())'s docs and the example from @soqb explicitly.

Dimonade avatar Jan 22 '23 17:01 Dimonade

Would it be reasonable to have a bundle! macro that automatically nest tuples when needed?

SkiFire13 avatar Jan 23 '23 11:01 SkiFire13

Would it be reasonable to have a bundle! macro that automatically nest tuples when needed?

Maybe? But it's still something users need to be aware of so that they know when to use such a macro. Learning about this limitation without a macro may help understand other places this crops up, such as with the number of system or query parameters.

Also, I don't know if we want to add a macro that really only serves to create nested tuples. If it created an "anonymous" local struct and had other functionality, it would probably be more useful.

MrGVSV avatar Jan 23 '23 15:01 MrGVSV

as far as I'm aware a macro would have more uses than just for bundles since the 15 limit is scattered all over since it is the arbitrary number picked to auto-implement tuples traits on tuples. It can be found in system parameters, query components & filters, and I'm sure more I'm forgetting. there has been some work put in to mitigate the problem when it comes to more common arias such as query components (you can make custom WorldQuery impls I think) to make them more readable/usable when large nesting would be needed or the same large groups are used a lot.

personalty I think it would be more useful if we could work out a way to present a custom error message telling people that this is a known limitation and how to fix it. no idea if you can do that with rust/bevy with how it currently is set up;

PhaestusFox avatar Jan 25 '23 05:01 PhaestusFox

I think this qualifies as "blocked on variadics": we want to fix this, but increasing the number of all-macros invocations will increase compile times and just move the goal posts.

alice-i-cecile avatar Jan 09 '24 15:01 alice-i-cecile

Can one of the fancy new Rust diagnostics on unimplemented traits help out here? It would be nice to just tell the user "If you have more than 15 things, nest your tuple".

janhohenheim avatar Jul 01 '24 19:07 janhohenheim