Skip to content

Conversation

@MrGVSV
Copy link
Member

@MrGVSV MrGVSV commented Mar 12, 2025

Objective

#13432 added proper reflection-based cloning. This is a better method than cloning via clone_value for reasons detailed in the description of that PR. However, it may not be immediately apparent to users why one should be used over the other, and what the gotchas of clone_value are.

Solution

This PR marks PartialReflect::clone_value as deprecated, with the deprecation notice pointing users to PartialReflect::reflect_clone. However, it also suggests using a new method introduced in this PR: PartialReflect::to_dynamic.

PartialReflect::to_dynamic is essentially a renaming of PartialReflect::clone_value. By naming it to_dynamic, we make it very obvious that what's returned is a dynamic type. The one caveat to this is that opaque types still use reflect_clone as they have no corresponding dynamic type.

Along with changing the name, the method is now optional, and comes with a default implementation that calls out to the respective reflection subtrait method. This was done because there was really no reason to require manual implementors provide a method that almost always calls out to a known set of methods.

Lastly, to make this default implementation work, this PR also did a similar thing with the clone_dynamic methods on the reflection subtraits. For example, Struct::clone_dynamic has been marked deprecated and is superseded by Struct::to_dynamic_struct. This was necessary to avoid the "multiple names in scope" issue.

Open Questions

This PR maintains the original signature of clone_value on to_dynamic. That is, it takes &self and returns Box<dyn PartialReflect>.

However, in order for this to work, it introduces a panic if the value is opaque and doesn't override the default reflect_clone implementation.

One thing we could do to avoid the panic would be to make the conversion fallible, either returning Option<Box<dyn PartialReflect>> or Result<Box<dyn PartialReflect>, ReflectCloneError>.

This makes using the method a little more involved (i.e. users have to either unwrap or handle the rare possibility of an error), but it would set us up for a world where opaque types don't strictly need to be Clone. Right now this bound is sort of implied by the fact that clone_value is a required trait method, and the default behavior of the macro is to use Clone for opaque types.

Alternatively, we could keep the signature but make the method required. This maintains that implied bound where manual implementors must provide some way of cloning the value (or YOLO it and just panic), but also makes the API simpler to use.

Finally, we could just leave it with the panic. It's unlikely this would occur in practice since our macro still requires Clone for opaque types, and thus this would only ever be an issue if someone were to manually implement PartialReflect without a valid to_dynamic or reflect_clone method.

Testing

You can test locally using the following command:

cargo test --package bevy_reflect --all-features

Migration Guide

PartialReflect::clone_value is being deprecated. Instead, use PartialReflect::to_dynamic if wanting to create a new dynamic instance of the reflected value. Alternatively, use PartialReflect::reflect_clone to attempt to create a true clone of the underlying value.

Similarly, the following methods have been deprecated and should be replaced with these alternatives:

  • Array::clone_dynamicArray::to_dynamic_array
  • Enum::clone_dynamicEnum::to_dynamic_enum
  • List::clone_dynamicList::to_dynamic_list
  • Map::clone_dynamicMap::to_dynamic_map
  • Set::clone_dynamicSet::to_dynamic_set
  • Struct::clone_dynamicStruct::to_dynamic_struct
  • Tuple::clone_dynamicTuple::to_dynamic_tuple
  • TupleStruct::clone_dynamicTupleStruct::to_dynamic_tuple_struct

@MrGVSV MrGVSV added C-Usability A targeted quality-of-life change that makes Bevy easier to use A-Reflection Runtime information about types D-Straightforward Simple bug fixes and API improvements, docs, test and examples S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Mar 12, 2025
@MrGVSV MrGVSV added this to the 0.16 milestone Mar 12, 2025
@MrGVSV MrGVSV moved this from Open to In Progress in Reflection Mar 12, 2025
Superseded by `PartialReflect::to_dynamic`.

This also deprecates all `clone_dynamic` subtrait methods with
more explicitly named ones
@MrGVSV MrGVSV force-pushed the mrgvsv/reflect/deprecate-clone_value branch 3 times, most recently from 7828585 to 0bf98ec Compare March 12, 2025 23:42
@alice-i-cecile alice-i-cecile added the M-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide label Mar 12, 2025
@MrGVSV MrGVSV force-pushed the mrgvsv/reflect/deprecate-clone_value branch from 0bf98ec to 81864c0 Compare March 13, 2025 00:21
Copy link
Contributor

@eugineerd eugineerd left a comment

Choose a reason for hiding this comment

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

Entity cloning changes look good to me and seem to increase performance for Reflect-based path in the benchmark by ~50% on my machine.

@alice-i-cecile alice-i-cecile added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Mar 13, 2025
@alice-i-cecile
Copy link
Member

@MrGVSV I'm comfortable merging this when CI is green; just say the word.

@MrGVSV
Copy link
Member Author

MrGVSV commented Mar 13, 2025

@MrGVSV I'm comfortable merging this when CI is green; just say the word.

Sounds good, I'll try to do that soon!

@MrGVSV MrGVSV force-pushed the mrgvsv/reflect/deprecate-clone_value branch from 81864c0 to f393d04 Compare March 14, 2025 06:26
@MrGVSV MrGVSV force-pushed the mrgvsv/reflect/deprecate-clone_value branch from f393d04 to 15d972a Compare March 14, 2025 06:51
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Mar 14, 2025
Merged via the queue into bevyengine:main with commit c2854a2 Mar 14, 2025
31 checks passed
@MrGVSV MrGVSV deleted the mrgvsv/reflect/deprecate-clone_value branch March 15, 2025 03:42
joshua-holmes pushed a commit to joshua-holmes/bevy that referenced this pull request Mar 24, 2025
# Objective

bevyengine#13432 added proper reflection-based cloning. This is a better method
than cloning via `clone_value` for reasons detailed in the description
of that PR. However, it may not be immediately apparent to users why one
should be used over the other, and what the gotchas of `clone_value`
are.

## Solution

This PR marks `PartialReflect::clone_value` as deprecated, with the
deprecation notice pointing users to `PartialReflect::reflect_clone`.
However, it also suggests using a new method introduced in this PR:
`PartialReflect::to_dynamic`.

`PartialReflect::to_dynamic` is essentially a renaming of
`PartialReflect::clone_value`. By naming it `to_dynamic`, we make it
very obvious that what's returned is a dynamic type. The one caveat to
this is that opaque types still use `reflect_clone` as they have no
corresponding dynamic type.

Along with changing the name, the method is now optional, and comes with
a default implementation that calls out to the respective reflection
subtrait method. This was done because there was really no reason to
require manual implementors provide a method that almost always calls
out to a known set of methods.

Lastly, to make this default implementation work, this PR also did a
similar thing with the `clone_dynamic ` methods on the reflection
subtraits. For example, `Struct::clone_dynamic` has been marked
deprecated and is superseded by `Struct::to_dynamic_struct`. This was
necessary to avoid the "multiple names in scope" issue.

### Open Questions

This PR maintains the original signature of `clone_value` on
`to_dynamic`. That is, it takes `&self` and returns `Box<dyn
PartialReflect>`.

However, in order for this to work, it introduces a panic if the value
is opaque and doesn't override the default `reflect_clone`
implementation.

One thing we could do to avoid the panic would be to make the conversion
fallible, either returning `Option<Box<dyn PartialReflect>>` or
`Result<Box<dyn PartialReflect>, ReflectCloneError>`.

This makes using the method a little more involved (i.e. users have to
either unwrap or handle the rare possibility of an error), but it would
set us up for a world where opaque types don't strictly need to be
`Clone`. Right now this bound is sort of implied by the fact that
`clone_value` is a required trait method, and the default behavior of
the macro is to use `Clone` for opaque types.

Alternatively, we could keep the signature but make the method required.
This maintains that implied bound where manual implementors must provide
some way of cloning the value (or YOLO it and just panic), but also
makes the API simpler to use.

Finally, we could just leave it with the panic. It's unlikely this would
occur in practice since our macro still requires `Clone` for opaque
types, and thus this would only ever be an issue if someone were to
manually implement `PartialReflect` without a valid `to_dynamic` or
`reflect_clone` method.

## Testing

You can test locally using the following command:

```
cargo test --package bevy_reflect --all-features
```

---

## Migration Guide

`PartialReflect::clone_value` is being deprecated. Instead, use
`PartialReflect::to_dynamic` if wanting to create a new dynamic instance
of the reflected value. Alternatively, use
`PartialReflect::reflect_clone` to attempt to create a true clone of the
underlying value.

Similarly, the following methods have been deprecated and should be
replaced with these alternatives:
- `Array::clone_dynamic` → `Array::to_dynamic_array`
- `Enum::clone_dynamic` → `Enum::to_dynamic_enum`
- `List::clone_dynamic` → `List::to_dynamic_list`
- `Map::clone_dynamic` → `Map::to_dynamic_map`
- `Set::clone_dynamic` → `Set::to_dynamic_set`
- `Struct::clone_dynamic` → `Struct::to_dynamic_struct`
- `Tuple::clone_dynamic` → `Tuple::to_dynamic_tuple`
- `TupleStruct::clone_dynamic` → `TupleStruct::to_dynamic_tuple_struct`
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-Usability A targeted quality-of-life change that makes Bevy easier to use D-Straightforward Simple bug fixes and API improvements, docs, test and examples M-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

3 participants