#How to do a query on an immutable World?

19 messages · Page 1 of 1 (latest)

young plaza
#

Hi,

Is there a way to do a Query on a immutable World? If yes, how?

I will illustrate with an example below.

This is a test I would like to be able to write:

fn test_empty_app_has_no_players() {
    let app = App::new();
    assert_eq!(count_n_players(&app), 0);
}

The idea of count_n_players is to count the number of times a (marker) component is present. Because we only read (i.e. do not modify the App), we can write let app (instead of let mut app).

Writing this test, however, fails when implementing count_n_players.

Below is an implementation that I wish I could write, that uses a Query on a (non-mut) World:

// Does not compile, as `query` expects a mutable World
fn count_n_players(app: &App) -> usize {
    let query = app.world().query::<&Player>();
    return query.iter(app.world()).len();
}

(there may be other implementations possible that do not use a query and I will give one below. My question, however, is about using queries on an immutable World)

However, a Query always needs a mutable World, hence an implementation that works is:

// Does not modify the App, I promise!
fn count_n_players(app: &mut App) -> usize {
    let mut query = app.world_mut().query::<&Player>();
    return query.iter(app.world_mut()).len();
}
#

I added a comment to illustrate that one needs to promise not to change an object, instead of enforcing it (i.e. not using mut).

With an implementation that uses &mut App, the test needs to be changed to:

fn test_empty_app_has_no_players() {
    let mut app = App::new();
    // Does not modify the App, I promise!
    assert_eq!(count_n_players(&mut app), 0);
}

Also here I added a comment to illustrate that one needs to promise not to change an object, instead of enforcing it (i.e. not using mut).

I assume that also in Bevy I express my promises in Rust, so how do I query something on an immutable App?

For completeness sake, below is an implementation that uses an immutable App that does not use a query. My question, however, is about using queries on an immutable word.

// I am uninterested in such alternative implementations:
// my question is about using a query
fn count_n_players(app: &App) -> usize {
    let mut n = 0;
    for c in app.world().components().iter() {
        if c.name().contains("Player") {
            n += 1;
        }
    }
    n
}

Thanks and cheers, Richel Bilderbeek

fading holly
young plaza
#

@fading holly thanks for the help!

I read in the doc: 'Returns an Iterator over the query results for the given World'. This means that the query is still needed and that query requires World to be mutable.

You think I misinterpreted? Thanks again!

fading holly
#

I don’t believe it or update_archetypes() require &mut World.

umbral kiln
fading holly
#

I don't understand why you're creating this limitation for yourself. But if I'm playing along, you could do this:

let (app, query) = { 
    let mut app = App::new();
    let mut q = app.world_mut().query...();
    query.update_archectypes(app.world());
    (app, q) 
};
for x in query.iter_manual(app.world()) {
    ...
}
young plaza
#

Hi @fading holly , thanks for the help, the problem in your suggestion is app.world_mut(): it is exactly what I try to avoid in my question.

To repeat the reasoning for this limitation: it is to be mut-correct and only use mut when I actually modify something, for example, as shown in the example code snippet:

fn test_empty_app_has_no_players() {
    let app = App::new();
    assert_eq!(count_n_players(&app), 0);
}

I hope that clarifies things 🙂 . Thanks for trying to help!

young plaza
umbral kiln
#

I think the mut is here for a reason, I'm very curious about the reason, my wild guess (and sorry If I spread misinformation), is that there is some state updated with the query, maybe queries are cached, or there's stat recorded per query, I'm not sure at all, it's probably worth a note on the documentation

#

(to answer exactly, I'm pretty sure devs would be very interested if a function can be "mut correct" ||but to reiterate, I'm not 100% sure it's possible there||)

umbral kiln
young plaza
umbral kiln
#

Remove the mut and follow the compiler to know more 😅

young plaza
young plaza
umbral kiln
#

Then remove the mut from query ; hacking into bevy codebase to answer our question of why it is necessary seems a good path forward (as I don’t have the answer 😢)

young plaza