Can a fn generate a tuple that can be used in legion World and CommandBuffer

As part of my rust journey of discovery I am writing a roguelike using the legion ECS library.
I am trying to see if there's a way I can refactor a spawner function so that tuple that is used within that function be called within a system.

In other words, currently have a bunch of functions that look like:

pub fn spawn_giant_rat(ecs: &mut World, pt: Point) {
  ecs.push(
    (
        Actor,
        Render{
            name: "Giant Rat".to_string(),
            tile: tile_index(13, 2),
            pt
        },
        FieldOfView::new(4),
 . . .

and ecs.push( is defined like:

pub fn push<T>(&mut self, components: T) -> Entity
where
    Option<T>: IntoComponentSource,
{ 

Unfortunately, IntoComponentSource is private in legion.
I'd like to refactor the function that I can return the tuple from one function and use in the existing function. Something like:

// Question: what would T look like here?
pub fn spawn_giant_rat_tuple(pt: Point) -> T { 
  (
    Actor,
    Render{ ... },
    ...
  )
 }

// So that I can pass it in here?
pub fn spawn_giant_rat(ecs: &mut World, pt: Point) {
  ecs.push(spawn_rat_tuble(pt));
}

Which version of legion are you using? I can do use legion::storage::IntoComponentSource; and get it just fine...

You should be able to write this:

pub fn spawn_giant_rat_tuple(pt: Point) -> impl legion::storage::IntoComponentSource { 
  Some((
    Actor,
    Render{ ... },
    ...
  ))
 }
1 Like

Thank you. That worked to get the fn spawn_giant_rat_tuple to compile. Do you have any guidance for the call on the ecs.push(spawn_giant_rat_tuple(pt)) side? I currently get:

error[E0277]: the trait bound `legion::internals::insert::Aos<impl 
legion::storage::IntoComponentSource, std::option::IntoIter<impl legion::storage::IntoComponentSource>>: legion::storage::ComponentSource` is not satisfied
  --> src/spawners/actors.rs:36:9
   |
36 |     ecs.push(spawn_rat_tuple(pt));
   |         ^^^^ the trait `legion::storage::ComponentSource` is not implemented for 
`legion::internals::insert::Aos<impl legion::storage::IntoComponentSource, std::option::IntoIter<impl legion::storage::IntoComponentSource>>`
   |
   = help: the following implementations were found:
         <legion::internals::insert::Aos<(), Iter> as legion::storage::ComponentSource>
         <legion::internals::insert::Aos<(A, B, C, D, E, F, G, H), Iter> as legion::storage::ComponentSource>
         <legion::internals::insert::Aos<(B, C, D, E, F, G, H), Iter> as legion::storage::ComponentSource>
         <legion::internals::insert::Aos<(C, D, E, F, G, H), Iter> as legion::storage::ComponentSource>
       and 5 others
   = note: required because of the requirements on the impl of `legion::storage::IntoComponentSource` for `std::option::Option<impl legion::storage::IntoComponentSource>`

No worries, if not. I'm researching now to try and understand the error message.

P.S. Not sure how my research into the legion code led me into the internals module rather than the storage module. Thanks for the clarificatino there. I'm using 0.3.1 of legion.

So that error is pointing out that there is no implementation of ComponentSource for Aos<impl IntoComponentSource, ..>. Apparently, it is implemented for all tuples in Aos<(A, B, ..), ..>. So I don't think you can actually avoid writing out the type in full. What I would do is just bite the bullet and do that:

pub fn spawn_giant_rat_tuple(pt: Point) -> (Actor, Render, ...) { 
  (
    Actor,
    Render{ ... },
    ...
  )
 }

That should make it go away. You can see the impls being generated here (Though the line numbers seem to be messed up.) This impl is needed to satisfy the bounds on the associated type IntoComponentSource::Source set by the World::push method.

There may be a way to do it like that with the right set of bounds or an extra trait impl somewhere, but I'm not seeing it.

1 Like

Cool. I was just trying to understand that file. It's a little verbose but not too bad, especially compared to writing two completely different functions for &World and &CommandBuffer. I can definitely live with it.

Thanks again for looking at this.

1 Like