Skip to content

Introduce ReflectConvert, a generic reflection mechanism for type conversions, intended for asset BSN.#23742

Open
pcwalton wants to merge 6 commits intobevyengine:mainfrom
pcwalton:reflect-convert
Open

Introduce ReflectConvert, a generic reflection mechanism for type conversions, intended for asset BSN.#23742
pcwalton wants to merge 6 commits intobevyengine:mainfrom
pcwalton:reflect-convert

Conversation

@pcwalton
Copy link
Copy Markdown
Contributor

@pcwalton pcwalton commented Apr 9, 2026

One of the important features of BSN is that supplying a value of type T where a value of type U is expected is allowed if a From conversion exists from T to U. This is what allows HandleTemplates to be automatically created from string literals. We need this feature for BSN like this to be valid:

SceneRoot("models/FlightHelmet/FlightHelmet.gltf#Scene0")

And for this to be valid:

EnvironmentMapLight {
    diffuse_map: "environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2",
    specular_map: "environment_maps/pisa_specular_rgb9e5_zstd.ktx2",
    intensity: 250.0,
}

Unfortunately, the From trait isn't reflectable, and this is a problem as reflection drives asset BSN. It's not immediately clear how to make From reflectable either, as a syntax like #[reflect(From)] wouldn't work as it's a generic trait.

Instead of making From directly reflectable, this patch introduces ReflectConvert, a new TypeData that encodes a generic way to convert a value of type T to type U. You register a type conversion like this:

App::new()
    .register_type::<i32>()
    .register_type::<String>()
    .register_type_conversion::<i32, String>(|n| Ok(n.into()));

And you invoke the conversion like this:

let reflect_convert = registry
    .get_type_data::<ReflectConvert>(TypeId::of::<String>())
    .unwrap();

let converted = reflect_convert
    .try_convert_from(Box::new(12345i32))
    .unwrap()
    .downcast::<String>()
    .unwrap();

I tested ReflectConvert with asset BSN (PR #23576). The ReflectConvert trait as implemented in this patch successfully enables HandleTemplates to be created from strings, without hard-coding HandleTemplate anywhere in the patch generation logic.

conversions, intended for asset BSN.

One of the important features of BSN is that supplying a value of type T
where a value of type U is expected is allowed if a `From` conversion
exists from T to U. This is what allows `HandleTemplate`s to be
automatically created from string literals. We need this feature for BSN
like this to be valid:

```rust
SceneRoot("models/FlightHelmet/FlightHelmet.gltf#Scene0")
```

And for this to be valid:

```rust
EnvironmentMapLight {
    diffuse_map: "environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2",
    specular_map: "environment_maps/pisa_specular_rgb9e5_zstd.ktx2",
    intensity: 250.0,
}
```

Unfortunately, the `From` trait isn't reflectable, and this is a problem
as reflection drives asset BSN. It's not immediately clear how to make
`From` reflectable either, as a syntax like `#[reflect(From)]` wouldn't
work as it's a generic trait.

Instead of making `From` directly reflectable, this patch introduces
`ReflectConvert`, a new `TypeData` that encodes a generic way to convert
a value of type T to type U. You register a type conversion like this:

```rust
App::new()
    .register_type::<i32>()
    .register_type::<String>()
    .register_type_conversion::<i32, String>(|n| Ok(n.into()));
```

And you invoke the conversion like this:

```rust
let reflect_convert = registry
    .get_type_data::<ReflectConvert>(TypeId::of::<String>())
    .unwrap();

let converted = reflect_convert
    .try_convert_from(Box::new(12345i32))
    .unwrap()
    .downcast::<String>()
    .unwrap();
```

I tested `ReflectConvert` with asset BSN (PR bevyengine#23576). The
`ReflectConvert` trait as implemented in this patch successfully enables
`HandleTemplate`s to be created from strings, without hard-coding
`HandleTemplate` anywhere in the patch generation logic.
@pcwalton pcwalton added the A-Reflection Runtime information about types label Apr 9, 2026
@github-project-automation github-project-automation bot moved this to Needs SME Triage in Reflection Apr 9, 2026
@pcwalton pcwalton added C-Feature A new feature, making something new possible D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Apr 9, 2026
///
/// See [`bevy_reflect::TypeRegistry::register_type_conversion`].
#[cfg(feature = "bevy_reflect")]
pub fn register_type_conversion<T, U>(&mut self, function: fn(T) -> Result<U, T>) -> &mut Self
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should have a register_into_type_conversion that just does the into for you? I suspect most calls to this will just be into.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was considering that too. In an earlier version you were just able to write .register_type_conversion::<T,U>(Into::into) but I realized that was incompatible with fallible conversions, and I'm pretty sure we do want fallible conversions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify: I meant provide both: the into is just a convenience for calling the more general TryInto

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added register_into_type_conversion as requested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Reflection Runtime information about types C-Feature A new feature, making something new possible D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

Status: Needs SME Triage

Development

Successfully merging this pull request may close these issues.

2 participants