Skip to content

Conversation

@MrGVSV
Copy link
Member

@MrGVSV MrGVSV commented Mar 9, 2025

Objective

This PR is a partial followup to #13432, but is not based on that branch as it also works as a completely standalone change.

The goal of this PR is to add reflection type data for the Clone trait.

For #13432, this is desired because it addresses inconsistencies mentioned in this comment (which are further addressed in #13723). Namely, we should try to reduce the split between special trait type data registration and regular trait type data registration.

Apart from that PR, this PR should allow users to create concrete clones within reflection much easier.

For reference, here are some ways cloning is typically handled:

// If the value is an `Opaque` type (e.g. primitives):
let clone: Box<dyn PartialReflect> = value.clone_value();

// If the value is any other type, we can use `ReflectFromReflect`:
let clone: Box<dyn PartialReflect> = {
  let info = value.get_represented_type_info().unwrap();
  let rfr = registry.get_type_data::<ReflectFromReflect>(info.type_id())).unwrap();
  rfr.from_reflect(&*value).unwrap()
};

// Or `ReflectDefault`:
let clone: Box<dyn PartialReflect> = {
  let info = value.get_represented_type_info().unwrap();
  let rd = registry.get_type_data::<ReflectDefault>(info.type_id())).unwrap();
  let default = rd.default();
  default.apply(&*value);
  default
};

Those solutions are also not ideal since some types do extra things when cloned (e.g. increasing a ref-count). It would be better if we could call that Clone impl directly via reflection.

Solution

This PR simply introduces a ReflectClone type data struct that can be registered like any other type data (assuming the type implements Clone).

This simplifies our above approach:

let clone: Box<dyn PartialReflect> = {
  let info = value.get_represented_type_info().unwrap();
  let rc = registry.get_type_data::<ReflectClone>(info.type_id()).unwrap();
  rc.clone(&*value)
};

Note that with #13432 this is even simpler, but this provides a solid alternative if needing to use type data (or if wanting to specially handle types that implement Clone).

Testing

You can test locally by running:

cargo test --doc "std_traits::ReflectClone::try_clone" --package bevy_reflect

Showcase

Reflected types that implement Clone can now register the ReflectClone type data, allowing for reflected values to be directly cloned.

#[derive(Reflect, Clone)]
#[reflect(Clone)]
struct MyStruct(String);

let mut registry = TypeRegistry::new();
registry.register::<MyStruct>();

let value: Box<dyn Reflect> = Box::new(MyStruct(String::from("Hello!")));

let reflect_clone = registry.get_type_data::<ReflectClone>(TypeId::of::<MyStruct>()).unwrap();

let clone: Box<dyn Reflect> = reflect_clone.clone(&*value);

assert!(value.reflect_partial_eq(&*clone).unwrap());

@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 9, 2025
@MrGVSV MrGVSV added this to Reflection Mar 9, 2025
@MrGVSV MrGVSV moved this to In Progress in Reflection Mar 9, 2025
@MrGVSV MrGVSV added this to the 0.16 milestone Mar 12, 2025
@MrGVSV
Copy link
Member Author

MrGVSV commented Mar 17, 2025

Note that with #18307 this will probably need to be rebased since it will cause a lot of errors where ReflectClone has not been imported.

@alice-i-cecile alice-i-cecile removed this from the 0.16 milestone Mar 17, 2025
@alice-i-cecile
Copy link
Member

Cutting from the milestone, but please feel free to explain why this is vital / part of a larger whole.

@MrGVSV
Copy link
Member Author

MrGVSV commented Mar 17, 2025

Cutting from the milestone, but please feel free to explain why this is vital / part of a larger whole.

That's no problem! The main reasons we want this in general are:

  • More consistent type registration behavior
  • Allows users to check if a type implements Clone at runtime

That last one is probably the more useful, but I guess I forgot to include it in the description haha.

@alice-i-cecile
Copy link
Member

Okay that makes sense to me :) We'll get this in eventually, but not critical to block on.

2 similar comments
@alice-i-cecile
Copy link
Member

Okay that makes sense to me :) We'll get this in eventually, but not critical to block on.

@alice-i-cecile
Copy link
Member

Okay that makes sense to me :) We'll get this in eventually, but not critical to block on.

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 S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

2 participants