Some help flattening a Bevy ECS Query?

So far, the only getting flattened is my brain, as I bang my head against the desk...

I'm trying to convert an ECS Query (as per Query), into a list Vector of tuples, and failing. The code looks like:

#[derive(Component, Debug)]
struct KeyMover {
    is_movable: bool
}

fn cycle_the_hat(mut tunnel_pos: Query<&mut KeyMover>) {
    let mut foo: Vec<&mut KeyMover> = tunnel_pos.iter_mut().collect();
    for beta in foo {
        println!("alpha: , beta: {:?}", beta);
    }
    // If I understand it, this query->access is identical to the one above - but works.
    for mut beta in &mut tunnel_pos {
        println!("beta: {:?}", beta);
    }
}

The error I'm getting:

The strange thing, it works fine if I take out the iter_mut() (and the Vector mut). And originally, I had other parameters to the Query, not mutable (like entity_id) - I thought having a mixed mutable/immutable set of parameters might be causing problems, but apparently not.

I've tried too many permutations of code here, including adding a turbofish to the collect(), but no joy so far. Any insight would be greatly appreciated. Or any other way of flattening a Query, if I'm missing something - the docs have a million options.

And one of the things I considered, is that it's a lifetime issue, but if so, I don't see how to fix it.

Another thing I tried, "unwrapping" the object:

Shockingly, it did not work. One thing that I don't understand, is where the Mut<_, KeyMover> is coming from, I'd have thought it should be Mut<KeyMover> (not that I can unwrap that, either).

use iter instead of iter_mut. The former iterates through the collection by returning references, while the latter is returning mutable references, and it seems that you are not after any kind of mutation here.

Ah, yeah, sorry, I am looking to alter KeyMover boolean, it just got dropped out in my simplification of the code. :slight_smile:

Try this way then:

for keyMover in tunnel_pos.iter_mut() {
    // mutate keyMover here
}

Well, I was trying to flatten the query into a Vector of (mutable) tuples, not just iterate through the query.

Well, the error message is clear – the iterator doesn't yield real mutable references, it yields some proxy type. That Mut is what you have to declare as your vector's element type (or leave it out and let the compiler infer it).

Huh... that worked. :slight_smile: I couldn't leave it out, or at least couldn't determine the syntax for an empty Vector, but putting in the proxy worked.

    let mut foo: Vec<(bevy::prelude::Entity, Mut<'_, KeyMover>)> = tunnel_pos.iter_mut().collect();

    for (entity_id, mut beta) in foo {
        println!("entity: {:?}, beta: {:?}", entity_id, beta);
        beta.is_movable = ! beta.is_movable;
    }

I didn't have to "unwrap" anything, either, to use it - I'm guessing that's how Lifetimes are attached to mutable variables? And the for-loop handled it.

Anyway, many thanks for the help.

That'd be Vec<_> (you can use _ anywhere a type is expected).

I'm not sure what you are getting at with "unwrapping", as I didn't suggest anything like that.

Hm - yep, the underscore also worked, thanks. The compiler did manage to correctly unpack the tuple.

As for "unwrapping", that was my initial thought on seeing the error with Mut<'_, KeyMover>, that it was something similar to Option/Result. I didn't really get, that the underscore is a Lifetime, and the variable can be just assigned, doesn't need any kind of unpacking.

Well, any variable/binding can be assigned to any value. If you have a Vec<Option<T>>, you can iterate over the optionals using for x in the_vec.

A Mut<'_, T> is a smart pointer which notes down whenever it's mutably dereferenced to enable change detection in Bevy. Hence, this can't just become a &'_ mut T.

1 Like