Skip to content

Commit

Permalink
refactor: Simplify lifetimes for Commands and related types (#11445)
Browse files Browse the repository at this point in the history
# Objective

It would be convenient to be able to call functions with `Commands` as a
parameter without having to move your own instance of `Commands`. Since
this struct is composed entirely of references, we can easily get an
owned instance of `Commands` by shortening the lifetime.

## Solution

Add `Commands::reborrow`, `EntiyCommands::reborrow`, and
`Deferred::reborrow`, which returns an owned version of themselves with
a shorter lifetime.

Remove unnecessary lifetimes from `EntityCommands`. The `'w` and `'s`
lifetimes only have to be separate for `Commands` because it's used as a
`SystemParam` -- this is not the case for `EntityCommands`.

---

## Changelog

Added `Commands::reborrow`. This is useful if you have `&mut Commands`
but need `Commands`. Also added `EntityCommands::reborrow` and
`Deferred:reborrow` which serve the same purpose.

## Migration Guide

The lifetimes for `EntityCommands` have been simplified.

```rust
// Before (Bevy 0.12)
struct MyStruct<'w, 's, 'a> {
     commands: EntityCommands<'w, 's, 'a>,
}

// After (Bevy 0.13)
struct MyStruct<'a> {
    commands: EntityCommands<'a>,
}
```

The method `EntityCommands::commands` now returns `Commands` rather than
`&mut Commands`.

```rust
// Before (Bevy 0.12)
let commands = entity_commands.commands();
commands.spawn(...);

// After (Bevy 0.13)
let mut commands = entity_commands.commands();
commands.spawn(...);
```
  • Loading branch information
JoJoJet authored Jan 22, 2024
1 parent e2e4e8e commit 7d69d31
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 23 deletions.
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/reflect/entity_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ pub trait ReflectCommandExt {
) -> &mut Self;
}

impl<'w, 's, 'a> ReflectCommandExt for EntityCommands<'w, 's, 'a> {
impl ReflectCommandExt for EntityCommands<'_> {
fn insert_reflect(&mut self, component: Box<dyn Reflect>) -> &mut Self {
self.commands.add(InsertReflect {
entity: self.entity,
Expand Down
60 changes: 47 additions & 13 deletions crates/bevy_ecs/src/system/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,31 @@ impl<'w, 's> Commands<'w, 's> {
}
}

/// Returns a [`Commands`] with a smaller lifetime.
/// This is useful if you have `&mut Commands` but need `Commands`.
///
/// # Examples
///
/// ```
/// # use bevy_ecs::prelude::*;
/// fn my_system(mut commands: Commands) {
/// // We do our initialization in a separate function,
/// // which expects an owned `Commands`.
/// do_initialization(commands.reborrow());
///
/// // Since we only reborrowed the commands instead of moving them, we can still use them.
/// commands.spawn_empty();
/// }
/// #
/// # fn do_initialization(_: Commands) {}
/// ```
pub fn reborrow(&mut self) -> Commands<'w, '_> {
Commands {
queue: self.queue.reborrow(),
entities: self.entities,
}
}

/// Take all commands from `other` and append them to `self`, leaving `other` empty
pub fn append(&mut self, other: &mut CommandQueue) {
self.queue.append(other);
Expand Down Expand Up @@ -186,11 +211,11 @@ impl<'w, 's> Commands<'w, 's> {
///
/// - [`spawn`](Self::spawn) to spawn an entity with a bundle.
/// - [`spawn_batch`](Self::spawn_batch) to spawn entities with a bundle each.
pub fn spawn_empty<'a>(&'a mut self) -> EntityCommands<'w, 's, 'a> {
pub fn spawn_empty(&mut self) -> EntityCommands {
let entity = self.entities.reserve_entity();
EntityCommands {
entity,
commands: self,
commands: self.reborrow(),
}
}

Expand All @@ -208,13 +233,13 @@ impl<'w, 's> Commands<'w, 's> {
/// [`Commands::spawn`]. This method should generally only be used for sharing entities across
/// apps, and only when they have a scheme worked out to share an ID space (which doesn't happen
/// by default).
pub fn get_or_spawn<'a>(&'a mut self, entity: Entity) -> EntityCommands<'w, 's, 'a> {
pub fn get_or_spawn(&mut self, entity: Entity) -> EntityCommands {
self.add(move |world: &mut World| {
world.get_or_spawn(entity);
});
EntityCommands {
entity,
commands: self,
commands: self.reborrow(),
}
}

Expand Down Expand Up @@ -268,7 +293,7 @@ impl<'w, 's> Commands<'w, 's> {
///
/// - [`spawn_empty`](Self::spawn_empty) to spawn an entity without any components.
/// - [`spawn_batch`](Self::spawn_batch) to spawn entities with a bundle each.
pub fn spawn<'a, T: Bundle>(&'a mut self, bundle: T) -> EntityCommands<'w, 's, 'a> {
pub fn spawn<T: Bundle>(&mut self, bundle: T) -> EntityCommands {
let mut e = self.spawn_empty();
e.insert(bundle);
e
Expand Down Expand Up @@ -310,7 +335,7 @@ impl<'w, 's> Commands<'w, 's> {
/// - [`get_entity`](Self::get_entity) for the fallible version.
#[inline]
#[track_caller]
pub fn entity<'a>(&'a mut self, entity: Entity) -> EntityCommands<'w, 's, 'a> {
pub fn entity(&mut self, entity: Entity) -> EntityCommands {
#[inline(never)]
#[cold]
#[track_caller]
Expand Down Expand Up @@ -359,10 +384,10 @@ impl<'w, 's> Commands<'w, 's> {
/// - [`entity`](Self::entity) for the panicking version.
#[inline]
#[track_caller]
pub fn get_entity<'a>(&'a mut self, entity: Entity) -> Option<EntityCommands<'w, 's, 'a>> {
pub fn get_entity(&mut self, entity: Entity) -> Option<EntityCommands> {
self.entities.contains(entity).then_some(EntityCommands {
entity,
commands: self,
commands: self.reborrow(),
})
}

Expand Down Expand Up @@ -674,12 +699,12 @@ where
}

/// A list of commands that will be run to modify an [entity](crate::entity).
pub struct EntityCommands<'w, 's, 'a> {
pub struct EntityCommands<'a> {
pub(crate) entity: Entity,
pub(crate) commands: &'a mut Commands<'w, 's>,
pub(crate) commands: Commands<'a, 'a>,
}

impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
impl EntityCommands<'_> {
/// Returns the [`Entity`] id of the entity.
///
/// # Example
Expand All @@ -698,6 +723,15 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
self.entity
}

/// Returns an [`EntityCommands`] with a smaller lifetime.
/// This is useful if you have `&mut EntityCommands` but you need `EntityCommands`.
pub fn reborrow(&mut self) -> EntityCommands {
EntityCommands {
entity: self.entity,
commands: self.commands.reborrow(),
}
}

/// Adds a [`Bundle`] of components to the entity.
///
/// This will overwrite any previous value(s) of the same component type.
Expand Down Expand Up @@ -956,8 +990,8 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
}

/// Returns the underlying [`Commands`].
pub fn commands(&mut self) -> &mut Commands<'w, 's> {
self.commands
pub fn commands(&mut self) -> Commands {
self.commands.reborrow()
}
}

Expand Down
8 changes: 8 additions & 0 deletions crates/bevy_ecs/src/system/system_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,14 @@ impl<'a, T: SystemBuffer> DerefMut for Deferred<'a, T> {
}
}

impl<T: SystemBuffer> Deferred<'_, T> {
/// Returns a [`Deferred<T>`] with a smaller lifetime.
/// This is useful if you have `&mut Deferred<T>` but need `Deferred<T>`.
pub fn reborrow(&mut self) -> Deferred<T> {
Deferred(self.0)
}
}

// SAFETY: Only local state is accessed.
unsafe impl<T: SystemBuffer> ReadOnlySystemParam for Deferred<'_, T> {}

Expand Down
14 changes: 7 additions & 7 deletions crates/bevy_hierarchy/src/child_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,23 +274,23 @@ impl Command for RemoveParent {
/// });
/// # }
/// ```
pub struct ChildBuilder<'w, 's, 'a> {
commands: &'a mut Commands<'w, 's>,
pub struct ChildBuilder<'a> {
commands: Commands<'a, 'a>,
push_children: PushChildren,
}

impl<'w, 's, 'a> ChildBuilder<'w, 's, 'a> {
impl ChildBuilder<'_> {
/// Spawns an entity with the given bundle and inserts it into the parent entity's [`Children`].
/// Also adds [`Parent`] component to the created entity.
pub fn spawn(&mut self, bundle: impl Bundle) -> EntityCommands<'w, 's, '_> {
pub fn spawn(&mut self, bundle: impl Bundle) -> EntityCommands {
let e = self.commands.spawn(bundle);
self.push_children.children.push(e.id());
e
}

/// Spawns an [`Entity`] with no components and inserts it into the parent entity's [`Children`].
/// Also adds [`Parent`] component to the created entity.
pub fn spawn_empty(&mut self) -> EntityCommands<'w, 's, '_> {
pub fn spawn_empty(&mut self) -> EntityCommands {
let e = self.commands.spawn_empty();
self.push_children.children.push(e.id());
e
Expand All @@ -302,7 +302,7 @@ impl<'w, 's, 'a> ChildBuilder<'w, 's, 'a> {
}

/// Adds a command to be executed, like [`Commands::add`].
pub fn add_command<C: Command + 'static>(&mut self, command: C) -> &mut Self {
pub fn add_command<C: Command>(&mut self, command: C) -> &mut Self {
self.commands.add(command);
self
}
Expand Down Expand Up @@ -374,7 +374,7 @@ pub trait BuildChildren {
fn remove_parent(&mut self) -> &mut Self;
}

impl<'w, 's, 'a> BuildChildren for EntityCommands<'w, 's, 'a> {
impl BuildChildren for EntityCommands<'_> {
fn with_children(&mut self, spawn_children: impl FnOnce(&mut ChildBuilder)) -> &mut Self {
let parent = self.id();
let mut builder = ChildBuilder {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_hierarchy/src/hierarchy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ pub trait DespawnRecursiveExt {
fn despawn_descendants(&mut self) -> &mut Self;
}

impl<'w, 's, 'a> DespawnRecursiveExt for EntityCommands<'w, 's, 'a> {
impl DespawnRecursiveExt for EntityCommands<'_> {
/// Despawns the provided entity and its children.
fn despawn_recursive(mut self) {
let entity = self.id();
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_transform/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ pub trait BuildChildrenTransformExt {
/// (during [`apply_deferred`](bevy_ecs::schedule::apply_deferred)).
fn remove_parent_in_place(&mut self) -> &mut Self;
}
impl<'w, 's, 'a> BuildChildrenTransformExt for EntityCommands<'w, 's, 'a> {
impl BuildChildrenTransformExt for EntityCommands<'_> {
fn set_parent_in_place(&mut self, parent: Entity) -> &mut Self {
let child = self.id();
self.commands().add(PushChildInPlace { child, parent });
Expand Down

0 comments on commit 7d69d31

Please sign in to comment.