bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
36.25k stars 3.58k forks source link

Lifetimes and `#[derive(WorldQuery)]` #9377

Open Zeenobit opened 1 year ago

Zeenobit commented 1 year ago

How can Bevy's documentation be improved?

https://docs.rs/bevy/latest/bevy/ecs/query/trait.WorldQuery.html

When using #[derive(WorldQuery)], based on the documentation, we should be using 'static lifetime for all references:

#[derive(WorldQuery)]
struct MyQuery {
    entity: Entity,
    // It is required that all reference lifetimes are explicitly annotated, just like in any
    // struct. Each lifetime should be 'static.
    component_a: &'static ComponentA,
    component_b: &'static ComponentB,
}

However, it seems like you can also use a lifetime parameter:

#[derive(WorldQuery)]
struct MyQuery<'a> {
    entity: Entity,
    component_a: &'a ComponentA,
    component_b: &'a ComponentB,
}

In fact, I've been using this lifetime parameter in my code (without knowing it should be 'static) to allow construction of WorldQuery types from &World. For example:

#[derive(WorldQuery)]
struct PlayerRef<'a> {
    pub entity: Entity,
    pub name: &'a Name,
    // ...
}
impl<'a> PlayerRef<'a> {
    pub fn from_entity_ref(entity: EntityRef<'a>) -> Option<PlayerRef<'a>> {
        // ...
    }
}

I find this pattern useful because it allows these query types be used in systems, as well as any code that has exclusive &World access (such as debug, tests, editor, etc.):

let player = PlayerRef::from_entity(world.entity(entity)).unwrap();
assert_eq!(player.name, ...)

Is this usage an anti-pattern?

JoJoJet commented 1 year ago

I have a somewhat related RFC: https://github.com/bevyengine/rfcs/pull/68.

This would make derived WorldQuery types use a lifetime parameter, which I think is far cleaner than using 'static lifetimes for everything, and results in fewer generated types.