Skip to main content

Crate bevy_entity_ptr

Crate bevy_entity_ptr 

Source
Expand description

Ergonomic smart-pointer-like access to Bevy ECS entities.

This crate provides two complementary approaches for accessing entity data with immutable-only semantics (functional programming style):

§Type Hierarchy

Entity (Bevy's raw type)
    │
    ├── EntityHandle (newtype, safe, explicit world param)
    │       └── .bind(world) → BoundEntity<'w> (scoped access)
    │
    └── WorldRef::entity() → EntityPtr (smart pointer, 'static world)

§Choosing Between Types

TypeSafetyErgonomicsUse When
EntityHandle✅ Fully safeExplicit world paramStore in components
BoundEntity<'w>✅ Fully safeScoped lifetimeSimple access, compiler-checked
EntityPtr✅ Safe API*No lifetime paramsTree/graph traversal, recursion

*One internal unsafe hidden by WorldExt extension trait

Recommendation: Start with BoundEntity<'w>. Use EntityPtr when lifetime annotations become cumbersome for complex traversal.

§Safe Approach: EntityHandle + BoundEntity

Use this when you want fully safe code with explicit world parameters:

use bevy_ecs::prelude::*;
use bevy_entity_ptr::{EntityHandle, BoundEntity};

#[derive(Component)]
struct Target(EntityHandle);

#[derive(Component)]
struct Name(&'static str);

fn my_system(world: &World, query: &Query<&Target>) {
    // EntityHandle stores compactly in components
    for target in query.iter() {
        // Bind to world for fluent access
        let bound = target.0.bind(world);
        if let Some(name) = bound.get::<Name>() {
            println!("Target: {}", name.0);
        }
    }
}

§Ergonomic Approach: WorldExt + EntityPtr

Use this when you want fluent traversal without passing &World everywhere. The WorldExt trait hides the internal unsafe, providing a clean API.

use bevy_ecs::prelude::*;
use bevy_entity_ptr::{WorldExt, EntityHandle};

#[derive(Component)]
struct Target(EntityHandle);

#[derive(Component)]
struct Name(&'static str);

fn traverse_system(world: &World) {
    // No unsafe needed! WorldExt provides ergonomic access
    for entity in world.iter_entities() {
        let ptr = world.entity_ptr(entity.id());

        // Follow references fluently
        if let Some(target) = ptr.follow::<Target, _>(|t| t.0) {
            if let Some(name) = target.get::<Name>() {
                println!("Target: {}", name.0);
            }
        }
    }
}

§Thread Safety

TypeSendSyncNotes
EntityHandleYesYesSafe to store in components
BoundEntityNoNoBorrows &World
WorldRefNoNoSystem-scoped only
EntityPtrNoNoSystem-scoped only

§Feature Flags

  • nav-traits: Enables HasParent and HasChildren traits for parent/child navigation

§Design Principles

  1. Immutable only - No get_mut variants (functional style)
  2. Single unsafe boundary - Only WorldRef::new() is unsafe
  3. Graceful stale handling - Despawned entities return None, not UB
  4. Zero-cost where possible - #[repr(transparent)], #[inline], const fn

Structs§

BoundEntity
An entity bound to a world reference for fluent, scoped access.
BoundEntityNav
Navigation wrapper for BoundEntity, providing parent/children traversal.
EntityHandle
A lightweight handle to an entity that can be stored in components.
EntityPtr
An ergonomic smart pointer to an entity with embedded World reference.
EntityPtrNav
Navigation wrapper for EntityPtr, providing parent/children traversal.
EntityPtrNavMany
Many-navigation wrapper for EntityPtr, for collecting multiple related entities.
WorldRef
A reference to a World with erased lifetime for ergonomic entity traversal.

Traits§

WorldExt
Extension trait for World providing ergonomic entity access methods.