bevy_reflect: Deprecate PartialReflect::clone_value
(#18284)
# 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_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`
This commit is contained in:
parent
adbb53b87f
commit
c2854a2a05
@ -10,7 +10,7 @@ use criterion::{
|
||||
criterion_group!(
|
||||
benches,
|
||||
concrete_list_apply,
|
||||
concrete_list_clone_dynamic,
|
||||
concrete_list_to_dynamic_list,
|
||||
dynamic_list_apply,
|
||||
dynamic_list_push
|
||||
);
|
||||
@ -81,20 +81,20 @@ fn concrete_list_apply(criterion: &mut Criterion) {
|
||||
list_apply(&mut group, "empty_base_concrete_patch", empty_base, patch);
|
||||
|
||||
list_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| {
|
||||
patch(size).clone_dynamic()
|
||||
patch(size).to_dynamic_list()
|
||||
});
|
||||
|
||||
list_apply(&mut group, "same_len_concrete_patch", full_base, patch);
|
||||
|
||||
list_apply(&mut group, "same_len_dynamic_patch", full_base, |size| {
|
||||
patch(size).clone_dynamic()
|
||||
patch(size).to_dynamic_list()
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn concrete_list_clone_dynamic(criterion: &mut Criterion) {
|
||||
let mut group = create_group(criterion, bench!("concrete_list_clone_dynamic"));
|
||||
fn concrete_list_to_dynamic_list(criterion: &mut Criterion) {
|
||||
let mut group = create_group(criterion, bench!("concrete_list_to_dynamic_list"));
|
||||
|
||||
for size in SIZES {
|
||||
group.throughput(Throughput::Elements(size as u64));
|
||||
@ -105,7 +105,7 @@ fn concrete_list_clone_dynamic(criterion: &mut Criterion) {
|
||||
|bencher, &size| {
|
||||
let v = iter::repeat_n(0, size).collect::<Vec<_>>();
|
||||
|
||||
bencher.iter(|| black_box(&v).clone_dynamic());
|
||||
bencher.iter(|| black_box(&v).to_dynamic_list());
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -127,7 +127,7 @@ fn dynamic_list_push(criterion: &mut Criterion) {
|
||||
let dst = DynamicList::default();
|
||||
|
||||
bencher.iter_batched(
|
||||
|| (src.clone(), dst.clone_dynamic()),
|
||||
|| (src.clone(), dst.to_dynamic_list()),
|
||||
|(src, mut dst)| {
|
||||
for item in src {
|
||||
dst.push(item);
|
||||
@ -145,20 +145,20 @@ fn dynamic_list_push(criterion: &mut Criterion) {
|
||||
fn dynamic_list_apply(criterion: &mut Criterion) {
|
||||
let mut group = create_group(criterion, bench!("dynamic_list_apply"));
|
||||
|
||||
let empty_base = |_: usize| || Vec::<u64>::new().clone_dynamic();
|
||||
let empty_base = |_: usize| || Vec::<u64>::new().to_dynamic_list();
|
||||
let full_base = |size: usize| move || iter::repeat_n(0, size).collect::<Vec<u64>>();
|
||||
let patch = |size: usize| iter::repeat_n(1, size).collect::<Vec<u64>>();
|
||||
|
||||
list_apply(&mut group, "empty_base_concrete_patch", empty_base, patch);
|
||||
|
||||
list_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| {
|
||||
patch(size).clone_dynamic()
|
||||
patch(size).to_dynamic_list()
|
||||
});
|
||||
|
||||
list_apply(&mut group, "same_len_concrete_patch", full_base, patch);
|
||||
|
||||
list_apply(&mut group, "same_len_dynamic_patch", full_base, |size| {
|
||||
patch(size).clone_dynamic()
|
||||
patch(size).to_dynamic_list()
|
||||
});
|
||||
|
||||
group.finish();
|
||||
|
@ -108,7 +108,7 @@ fn concrete_map_apply(criterion: &mut Criterion) {
|
||||
);
|
||||
|
||||
map_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| {
|
||||
key_range_patch(size).clone_dynamic()
|
||||
key_range_patch(size).to_dynamic_map()
|
||||
});
|
||||
|
||||
map_apply(
|
||||
@ -122,7 +122,7 @@ fn concrete_map_apply(criterion: &mut Criterion) {
|
||||
&mut group,
|
||||
"same_keys_dynamic_patch",
|
||||
key_range_base,
|
||||
|size| key_range_patch(size).clone_dynamic(),
|
||||
|size| key_range_patch(size).to_dynamic_map(),
|
||||
);
|
||||
|
||||
map_apply(
|
||||
@ -136,7 +136,7 @@ fn concrete_map_apply(criterion: &mut Criterion) {
|
||||
&mut group,
|
||||
"disjoint_keys_dynamic_patch",
|
||||
key_range_base,
|
||||
|size| disjoint_patch(size).clone_dynamic(),
|
||||
|size| disjoint_patch(size).to_dynamic_map(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -159,7 +159,7 @@ fn dynamic_map_apply(criterion: &mut Criterion) {
|
||||
(0..size as u64)
|
||||
.zip(iter::repeat(0))
|
||||
.collect::<HashMap<u64, u64>>()
|
||||
.clone_dynamic()
|
||||
.to_dynamic_map()
|
||||
}
|
||||
};
|
||||
|
||||
@ -183,7 +183,7 @@ fn dynamic_map_apply(criterion: &mut Criterion) {
|
||||
);
|
||||
|
||||
map_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| {
|
||||
key_range_patch(size).clone_dynamic()
|
||||
key_range_patch(size).to_dynamic_map()
|
||||
});
|
||||
|
||||
map_apply(
|
||||
@ -197,7 +197,7 @@ fn dynamic_map_apply(criterion: &mut Criterion) {
|
||||
&mut group,
|
||||
"same_keys_dynamic_patch",
|
||||
key_range_base,
|
||||
|size| key_range_patch(size).clone_dynamic(),
|
||||
|size| key_range_patch(size).to_dynamic_map(),
|
||||
);
|
||||
|
||||
map_apply(
|
||||
@ -211,7 +211,7 @@ fn dynamic_map_apply(criterion: &mut Criterion) {
|
||||
&mut group,
|
||||
"disjoint_keys_dynamic_patch",
|
||||
key_range_base,
|
||||
|size| disjoint_patch(size).clone_dynamic(),
|
||||
|size| disjoint_patch(size).to_dynamic_map(),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,8 @@ criterion_group!(
|
||||
concrete_struct_apply,
|
||||
concrete_struct_field,
|
||||
concrete_struct_type_info,
|
||||
concrete_struct_clone,
|
||||
dynamic_struct_clone,
|
||||
concrete_struct_to_dynamic_struct,
|
||||
dynamic_struct_to_dynamic_struct,
|
||||
dynamic_struct_apply,
|
||||
dynamic_struct_get_field,
|
||||
dynamic_struct_insert,
|
||||
@ -113,7 +113,7 @@ fn concrete_struct_apply(criterion: &mut Criterion) {
|
||||
bencher.iter_batched(
|
||||
|| {
|
||||
let (obj, _) = input();
|
||||
let patch = obj.clone_dynamic();
|
||||
let patch = obj.to_dynamic_struct();
|
||||
(obj, patch)
|
||||
},
|
||||
|(mut obj, patch)| obj.apply(black_box(&patch)),
|
||||
@ -170,8 +170,8 @@ fn concrete_struct_type_info(criterion: &mut Criterion) {
|
||||
}
|
||||
}
|
||||
|
||||
fn concrete_struct_clone(criterion: &mut Criterion) {
|
||||
let mut group = create_group(criterion, bench!("concrete_struct_clone"));
|
||||
fn concrete_struct_to_dynamic_struct(criterion: &mut Criterion) {
|
||||
let mut group = create_group(criterion, bench!("concrete_struct_to_dynamic_struct"));
|
||||
|
||||
let structs: [(Box<dyn Struct>, Box<dyn Struct>); 5] = [
|
||||
(
|
||||
@ -203,28 +203,28 @@ fn concrete_struct_clone(criterion: &mut Criterion) {
|
||||
BenchmarkId::new("NonGeneric", field_count),
|
||||
&standard,
|
||||
|bencher, s| {
|
||||
bencher.iter(|| s.clone_dynamic());
|
||||
bencher.iter(|| s.to_dynamic_struct());
|
||||
},
|
||||
);
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("Generic", field_count),
|
||||
&generic,
|
||||
|bencher, s| {
|
||||
bencher.iter(|| s.clone_dynamic());
|
||||
bencher.iter(|| s.to_dynamic_struct());
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn dynamic_struct_clone(criterion: &mut Criterion) {
|
||||
let mut group = create_group(criterion, bench!("dynamic_struct_clone"));
|
||||
fn dynamic_struct_to_dynamic_struct(criterion: &mut Criterion) {
|
||||
let mut group = create_group(criterion, bench!("dynamic_struct_to_dynamic_struct"));
|
||||
|
||||
let structs: [Box<dyn Struct>; 5] = [
|
||||
Box::new(Struct1::default().clone_dynamic()),
|
||||
Box::new(Struct16::default().clone_dynamic()),
|
||||
Box::new(Struct32::default().clone_dynamic()),
|
||||
Box::new(Struct64::default().clone_dynamic()),
|
||||
Box::new(Struct128::default().clone_dynamic()),
|
||||
Box::new(Struct1::default().to_dynamic_struct()),
|
||||
Box::new(Struct16::default().to_dynamic_struct()),
|
||||
Box::new(Struct32::default().to_dynamic_struct()),
|
||||
Box::new(Struct64::default().to_dynamic_struct()),
|
||||
Box::new(Struct128::default().to_dynamic_struct()),
|
||||
];
|
||||
|
||||
for s in structs {
|
||||
@ -234,7 +234,7 @@ fn dynamic_struct_clone(criterion: &mut Criterion) {
|
||||
BenchmarkId::from_parameter(field_count),
|
||||
&s,
|
||||
|bencher, s| {
|
||||
bencher.iter(|| s.clone_dynamic());
|
||||
bencher.iter(|| s.to_dynamic_struct());
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -265,7 +265,7 @@ fn dynamic_struct_apply(criterion: &mut Criterion) {
|
||||
&patch,
|
||||
|bencher, patch| {
|
||||
bencher.iter_batched(
|
||||
|| (base.clone_dynamic(), patch()),
|
||||
|| (base.to_dynamic_struct(), patch()),
|
||||
|(mut base, patch)| base.apply(black_box(&*patch)),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
@ -289,7 +289,7 @@ fn dynamic_struct_apply(criterion: &mut Criterion) {
|
||||
}
|
||||
|
||||
bencher.iter_batched(
|
||||
|| base.clone_dynamic(),
|
||||
|| base.to_dynamic_struct(),
|
||||
|mut base| base.apply(black_box(&patch)),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
@ -315,7 +315,7 @@ fn dynamic_struct_insert(criterion: &mut Criterion) {
|
||||
|
||||
let field = format!("field_{}", field_count);
|
||||
bencher.iter_batched(
|
||||
|| s.clone_dynamic(),
|
||||
|| s.to_dynamic_struct(),
|
||||
|mut s| {
|
||||
s.insert(black_box(&field), ());
|
||||
},
|
||||
|
@ -658,7 +658,7 @@ mod tests {
|
||||
assert_eq!(UntypedHandle::from(typed.clone()), untyped);
|
||||
}
|
||||
|
||||
/// `Reflect::clone_value` should increase the strong count of a strong handle
|
||||
/// `PartialReflect::reflect_clone`/`PartialReflect::to_dynamic` should increase the strong count of a strong handle
|
||||
#[test]
|
||||
fn strong_handle_reflect_clone() {
|
||||
use crate::{AssetApp, AssetPlugin, Assets, VisitAssetDependencies};
|
||||
@ -689,7 +689,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let reflected: &dyn Reflect = &handle;
|
||||
let cloned_handle: Box<dyn PartialReflect> = reflected.clone_value();
|
||||
let _cloned_handle: Box<dyn Reflect> = reflected.reflect_clone().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Arc::strong_count(strong),
|
||||
@ -697,10 +697,18 @@ mod tests {
|
||||
"Cloning the handle with reflect should increase the strong count to 2"
|
||||
);
|
||||
|
||||
let from_reflect_handle: Handle<MyAsset> =
|
||||
FromReflect::from_reflect(&*cloned_handle).unwrap();
|
||||
let dynamic_handle: Box<dyn PartialReflect> = reflected.to_dynamic();
|
||||
|
||||
assert_eq!(Arc::strong_count(strong), 3, "Converting the reflected value back to a handle should increase the strong count to 3");
|
||||
assert_eq!(
|
||||
Arc::strong_count(strong),
|
||||
3,
|
||||
"Converting the handle to a dynamic should increase the strong count to 3"
|
||||
);
|
||||
|
||||
let from_reflect_handle: Handle<MyAsset> =
|
||||
FromReflect::from_reflect(&*dynamic_handle).unwrap();
|
||||
|
||||
assert_eq!(Arc::strong_count(strong), 4, "Converting the reflected value back to a handle should increase the strong count to 4");
|
||||
assert!(
|
||||
from_reflect_handle.is_strong(),
|
||||
"The cloned handle should still be strong"
|
||||
|
@ -2911,12 +2911,14 @@ pub fn component_clone_via_clone<C: Clone + Component>(
|
||||
/// - Component has [`TypeId`]
|
||||
/// - Component is registered
|
||||
/// - Component has [`ReflectFromPtr`](bevy_reflect::ReflectFromPtr) registered
|
||||
/// - Component has one of the following registered: [`ReflectFromReflect`](bevy_reflect::ReflectFromReflect),
|
||||
/// - Component can be cloned via [`PartialReflect::reflect_clone`] _or_ has one of the following registered: [`ReflectFromReflect`](bevy_reflect::ReflectFromReflect),
|
||||
/// [`ReflectDefault`](bevy_reflect::std_traits::ReflectDefault), [`ReflectFromWorld`](crate::reflect::ReflectFromWorld)
|
||||
///
|
||||
/// If any of the conditions is not satisfied, the component will be skipped.
|
||||
///
|
||||
/// See [`EntityClonerBuilder`](crate::entity::EntityClonerBuilder) for details.
|
||||
///
|
||||
/// [`PartialReflect::reflect_clone`]: bevy_reflect::PartialReflect::reflect_clone
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
pub fn component_clone_via_reflect(
|
||||
commands: &mut Commands,
|
||||
@ -2934,6 +2936,21 @@ pub fn component_clone_via_reflect(
|
||||
// checked in read_source_component_reflect
|
||||
let type_id = component_info.type_id().unwrap();
|
||||
|
||||
// Try to clone using `reflect_clone`
|
||||
if let Ok(mut component) = source_component_reflect.reflect_clone() {
|
||||
if let Some(reflect_component) =
|
||||
registry.get_type_data::<crate::reflect::ReflectComponent>(type_id)
|
||||
{
|
||||
reflect_component.visit_entities_mut(&mut *component, &mut |entity| {
|
||||
*entity = ctx.entity_mapper().get_mapped(*entity);
|
||||
});
|
||||
}
|
||||
drop(registry);
|
||||
|
||||
ctx.write_target_component_reflect(component);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to clone using ReflectFromReflect
|
||||
if let Some(reflect_from_reflect) =
|
||||
registry.get_type_data::<bevy_reflect::ReflectFromReflect>(type_id)
|
||||
@ -2977,7 +2994,7 @@ pub fn component_clone_via_reflect(
|
||||
mapped_entities.push(entity);
|
||||
});
|
||||
}
|
||||
let source_component_cloned = source_component_reflect.clone_value();
|
||||
let source_component_cloned = source_component_reflect.to_dynamic();
|
||||
let component_layout = component_info.layout();
|
||||
let target = ctx.target();
|
||||
let component_id = ctx.component_id();
|
||||
@ -3008,7 +3025,11 @@ pub fn component_clone_via_reflect(
|
||||
world
|
||||
.entity_mut(target)
|
||||
.insert_by_id(component_id, OwningPtr::new(raw_component_ptr));
|
||||
alloc::alloc::dealloc(raw_component_ptr.as_ptr(), component_layout);
|
||||
|
||||
if component_layout.size() > 0 {
|
||||
// Ensure we don't attempt to deallocate zero-sized components
|
||||
alloc::alloc::dealloc(raw_component_ptr.as_ptr(), component_layout);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -256,7 +256,11 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> {
|
||||
);
|
||||
self.bundle_scratch
|
||||
.push_ptr(self.component_id, PtrMut::new(target_component_data_ptr));
|
||||
alloc::alloc::dealloc(component_data_ptr, component_layout);
|
||||
|
||||
if component_layout.size() > 0 {
|
||||
// Ensure we don't attempt to deallocate zero-sized components
|
||||
alloc::alloc::dealloc(component_data_ptr, component_layout);
|
||||
}
|
||||
}
|
||||
|
||||
self.target_component_written = true;
|
||||
@ -848,6 +852,7 @@ mod tests {
|
||||
use alloc::vec::Vec;
|
||||
use bevy_ptr::OwningPtr;
|
||||
use bevy_reflect::Reflect;
|
||||
use core::marker::PhantomData;
|
||||
use core::{alloc::Layout, ops::Deref};
|
||||
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
@ -888,67 +893,95 @@ mod tests {
|
||||
assert!(world.get::<A>(e_clone).is_some_and(|c| *c == component));
|
||||
}
|
||||
|
||||
// TODO: remove this when https://github.com/bevyengine/bevy/pull/13432 lands
|
||||
#[test]
|
||||
fn clone_entity_using_reflect_all_paths() {
|
||||
// `ReflectDefault`-based fast path
|
||||
#[derive(PartialEq, Eq, Default, Debug)]
|
||||
struct NotClone;
|
||||
|
||||
// `reflect_clone`-based fast path
|
||||
#[derive(Component, Reflect, PartialEq, Eq, Default, Debug)]
|
||||
#[reflect(Default)]
|
||||
#[reflect(from_reflect = false)]
|
||||
struct A {
|
||||
field: usize,
|
||||
field2: Vec<usize>,
|
||||
}
|
||||
|
||||
// `ReflectFromReflect`-based fast path
|
||||
// `ReflectDefault`-based fast path
|
||||
#[derive(Component, Reflect, PartialEq, Eq, Default, Debug)]
|
||||
#[reflect(Default)]
|
||||
#[reflect(from_reflect = false)]
|
||||
struct B {
|
||||
field: usize,
|
||||
field2: Vec<usize>,
|
||||
#[reflect(ignore)]
|
||||
ignored: NotClone,
|
||||
}
|
||||
|
||||
// `ReflectFromReflect`-based fast path
|
||||
#[derive(Component, Reflect, PartialEq, Eq, Default, Debug)]
|
||||
struct C {
|
||||
field: usize,
|
||||
field2: Vec<usize>,
|
||||
#[reflect(ignore)]
|
||||
ignored: NotClone,
|
||||
}
|
||||
|
||||
// `ReflectFromWorld`-based fast path
|
||||
#[derive(Component, Reflect, PartialEq, Eq, Default, Debug)]
|
||||
#[reflect(FromWorld)]
|
||||
#[reflect(from_reflect = false)]
|
||||
struct C {
|
||||
struct D {
|
||||
field: usize,
|
||||
field2: Vec<usize>,
|
||||
#[reflect(ignore)]
|
||||
ignored: NotClone,
|
||||
}
|
||||
|
||||
let mut world = World::default();
|
||||
world.init_resource::<AppTypeRegistry>();
|
||||
let registry = world.get_resource::<AppTypeRegistry>().unwrap();
|
||||
registry.write().register::<(A, B, C)>();
|
||||
registry.write().register::<(A, B, C, D)>();
|
||||
|
||||
let a_id = world.register_component::<A>();
|
||||
let b_id = world.register_component::<B>();
|
||||
let c_id = world.register_component::<C>();
|
||||
let d_id = world.register_component::<D>();
|
||||
let component_a = A {
|
||||
field: 5,
|
||||
field2: vec![1, 2, 3, 4, 5],
|
||||
};
|
||||
let component_b = B {
|
||||
field: 6,
|
||||
field: 5,
|
||||
field2: vec![1, 2, 3, 4, 5],
|
||||
ignored: NotClone,
|
||||
};
|
||||
let component_c = C {
|
||||
field: 6,
|
||||
field2: vec![1, 2, 3, 4, 5],
|
||||
ignored: NotClone,
|
||||
};
|
||||
let component_d = D {
|
||||
field: 7,
|
||||
field2: vec![1, 2, 3, 4, 5],
|
||||
ignored: NotClone,
|
||||
};
|
||||
|
||||
let e = world.spawn((component_a, component_b, component_c)).id();
|
||||
let e = world
|
||||
.spawn((component_a, component_b, component_c, component_d))
|
||||
.id();
|
||||
let e_clone = world.spawn_empty().id();
|
||||
|
||||
EntityCloner::build(&mut world)
|
||||
.override_clone_behavior_with_id(a_id, ComponentCloneBehavior::reflect())
|
||||
.override_clone_behavior_with_id(b_id, ComponentCloneBehavior::reflect())
|
||||
.override_clone_behavior_with_id(c_id, ComponentCloneBehavior::reflect())
|
||||
.override_clone_behavior_with_id(d_id, ComponentCloneBehavior::reflect())
|
||||
.clone_entity(e, e_clone);
|
||||
|
||||
assert_eq!(world.get::<A>(e_clone), Some(world.get::<A>(e).unwrap()));
|
||||
assert_eq!(world.get::<B>(e_clone), Some(world.get::<B>(e).unwrap()));
|
||||
assert_eq!(world.get::<C>(e_clone), Some(world.get::<C>(e).unwrap()));
|
||||
assert_eq!(world.get::<D>(e_clone), Some(world.get::<D>(e).unwrap()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1025,16 +1058,16 @@ mod tests {
|
||||
#[derive(Component, PartialEq, Eq, Default, Debug)]
|
||||
struct A;
|
||||
|
||||
// No valid type data
|
||||
// No valid type data and not `reflect_clone`-able
|
||||
#[derive(Component, Reflect, PartialEq, Eq, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
#[reflect(from_reflect = false)]
|
||||
struct B;
|
||||
struct B(#[reflect(ignore)] PhantomData<()>);
|
||||
|
||||
let mut world = World::default();
|
||||
|
||||
// No AppTypeRegistry
|
||||
let e = world.spawn((A, B)).id();
|
||||
let e = world.spawn((A, B(Default::default()))).id();
|
||||
let e_clone = world.spawn_empty().id();
|
||||
EntityCloner::build(&mut world)
|
||||
.override_clone_behavior::<A>(ComponentCloneBehavior::reflect())
|
||||
@ -1048,7 +1081,7 @@ mod tests {
|
||||
let registry = world.get_resource::<AppTypeRegistry>().unwrap();
|
||||
registry.write().register::<B>();
|
||||
|
||||
let e = world.spawn((A, B)).id();
|
||||
let e = world.spawn((A, B(Default::default()))).id();
|
||||
let e_clone = world.spawn_empty().id();
|
||||
EntityCloner::build(&mut world).clone_entity(e, e_clone);
|
||||
assert_eq!(world.get::<A>(e_clone), None);
|
||||
@ -1347,7 +1380,11 @@ mod tests {
|
||||
fn clone_with_reflect_from_world() {
|
||||
#[derive(Component, Reflect, PartialEq, Eq, Debug)]
|
||||
#[reflect(Component, FromWorld, from_reflect = false)]
|
||||
struct SomeRef(#[entities] Entity);
|
||||
struct SomeRef(
|
||||
#[entities] Entity,
|
||||
// We add an ignored field here to ensure `reflect_clone` fails and `FromWorld` is used
|
||||
#[reflect(ignore)] PhantomData<()>,
|
||||
);
|
||||
|
||||
#[derive(Resource)]
|
||||
struct FromWorldCalled(bool);
|
||||
@ -1355,7 +1392,7 @@ mod tests {
|
||||
impl FromWorld for SomeRef {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
world.insert_resource(FromWorldCalled(true));
|
||||
SomeRef(Entity::PLACEHOLDER)
|
||||
SomeRef(Entity::PLACEHOLDER, Default::default())
|
||||
}
|
||||
}
|
||||
let mut world = World::new();
|
||||
@ -1365,14 +1402,17 @@ mod tests {
|
||||
|
||||
let a = world.spawn_empty().id();
|
||||
let b = world.spawn_empty().id();
|
||||
let c = world.spawn(SomeRef(a)).id();
|
||||
let c = world.spawn(SomeRef(a, Default::default())).id();
|
||||
let d = world.spawn_empty().id();
|
||||
let mut map = EntityHashMap::<Entity>::new();
|
||||
map.insert(a, b);
|
||||
map.insert(c, d);
|
||||
|
||||
let cloned = EntityCloner::default().clone_entity_mapped(&mut world, c, &mut map);
|
||||
assert_eq!(*world.entity(cloned).get::<SomeRef>().unwrap(), SomeRef(b));
|
||||
assert_eq!(
|
||||
*world.entity(cloned).get::<SomeRef>().unwrap(),
|
||||
SomeRef(b, Default::default())
|
||||
);
|
||||
assert!(world.resource::<FromWorldCalled>().0);
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ pub trait ReflectCommandExt {
|
||||
/// // use the insert_reflect entity command to insert that component/bundle into an entity.
|
||||
/// commands
|
||||
/// .spawn_empty()
|
||||
/// .insert_reflect(prefab.data.clone_value());
|
||||
/// .insert_reflect(prefab.data.reflect_clone().unwrap().into_partial_reflect());
|
||||
/// }
|
||||
/// ```
|
||||
fn insert_reflect(&mut self, component: Box<dyn PartialReflect>) -> &mut Self;
|
||||
@ -442,21 +442,30 @@ mod tests {
|
||||
|
||||
let entity = commands.spawn_empty().id();
|
||||
let entity2 = commands.spawn_empty().id();
|
||||
let entity3 = commands.spawn_empty().id();
|
||||
|
||||
let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn PartialReflect>;
|
||||
let boxed_reflect_component_a_clone = boxed_reflect_component_a.clone_value();
|
||||
let boxed_reflect_component_a_clone = boxed_reflect_component_a.reflect_clone().unwrap();
|
||||
let boxed_reflect_component_a_dynamic = boxed_reflect_component_a.to_dynamic();
|
||||
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert_reflect(boxed_reflect_component_a);
|
||||
commands
|
||||
.entity(entity2)
|
||||
.insert_reflect(boxed_reflect_component_a_clone);
|
||||
.insert_reflect(boxed_reflect_component_a_clone.into_partial_reflect());
|
||||
commands
|
||||
.entity(entity3)
|
||||
.insert_reflect(boxed_reflect_component_a_dynamic);
|
||||
system_state.apply(&mut world);
|
||||
|
||||
assert_eq!(
|
||||
world.entity(entity).get::<ComponentA>(),
|
||||
world.entity(entity2).get::<ComponentA>()
|
||||
world.entity(entity2).get::<ComponentA>(),
|
||||
);
|
||||
assert_eq!(
|
||||
world.entity(entity).get::<ComponentA>(),
|
||||
world.entity(entity3).get::<ComponentA>(),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
|
||||
}
|
||||
}
|
||||
|
||||
fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicEnum {
|
||||
fn to_dynamic_enum(&self) -> #bevy_reflect_path::DynamicEnum {
|
||||
#bevy_reflect_path::DynamicEnum::from_ref::<Self>(self)
|
||||
}
|
||||
}
|
||||
@ -186,11 +186,6 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream
|
||||
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_value(&self) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::PartialReflect> {
|
||||
#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#bevy_reflect_path::Enum::clone_dynamic(self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_apply(
|
||||
&mut self,
|
||||
|
@ -78,7 +78,7 @@ pub(crate) fn impl_opaque(meta: &ReflectMeta) -> proc_macro2::TokenStream {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_value(&self) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::PartialReflect> {
|
||||
fn to_dynamic(&self) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::PartialReflect> {
|
||||
#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#FQClone::clone(self))
|
||||
}
|
||||
|
||||
|
@ -120,10 +120,10 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS
|
||||
#bevy_reflect_path::FieldIter::new(self)
|
||||
}
|
||||
|
||||
fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicStruct {
|
||||
fn to_dynamic_struct(&self) -> #bevy_reflect_path::DynamicStruct {
|
||||
let mut dynamic: #bevy_reflect_path::DynamicStruct = #FQDefault::default();
|
||||
dynamic.set_represented_type(#bevy_reflect_path::PartialReflect::get_represented_type_info(self));
|
||||
#(dynamic.insert_boxed(#field_names, #bevy_reflect_path::PartialReflect::clone_value(#fields_ref));)*
|
||||
#(dynamic.insert_boxed(#field_names, #bevy_reflect_path::PartialReflect::to_dynamic(#fields_ref));)*
|
||||
dynamic
|
||||
}
|
||||
}
|
||||
@ -134,11 +134,6 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS
|
||||
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_value(&self) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::PartialReflect> {
|
||||
#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#bevy_reflect_path::Struct::clone_dynamic(self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_apply(
|
||||
&mut self,
|
||||
|
@ -87,10 +87,10 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::
|
||||
#bevy_reflect_path::TupleStructFieldIter::new(self)
|
||||
}
|
||||
|
||||
fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicTupleStruct {
|
||||
fn to_dynamic_tuple_struct(&self) -> #bevy_reflect_path::DynamicTupleStruct {
|
||||
let mut dynamic: #bevy_reflect_path::DynamicTupleStruct = #FQDefault::default();
|
||||
dynamic.set_represented_type(#bevy_reflect_path::PartialReflect::get_represented_type_info(self));
|
||||
#(dynamic.insert_boxed(#bevy_reflect_path::PartialReflect::clone_value(#fields_ref));)*
|
||||
#(dynamic.insert_boxed(#bevy_reflect_path::PartialReflect::to_dynamic(#fields_ref));)*
|
||||
dynamic
|
||||
}
|
||||
}
|
||||
@ -100,10 +100,6 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::
|
||||
fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> {
|
||||
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
|
||||
}
|
||||
#[inline]
|
||||
fn clone_value(&self) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box<dyn #bevy_reflect_path::PartialReflect> {
|
||||
#bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#bevy_reflect_path::TupleStruct::clone_dynamic(self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_apply(
|
||||
|
@ -69,10 +69,16 @@ pub trait Array: PartialReflect {
|
||||
fn drain(self: Box<Self>) -> Vec<Box<dyn PartialReflect>>;
|
||||
|
||||
/// Clones the list, producing a [`DynamicArray`].
|
||||
#[deprecated(since = "0.16.0", note = "use `to_dynamic_array` instead")]
|
||||
fn clone_dynamic(&self) -> DynamicArray {
|
||||
self.to_dynamic_array()
|
||||
}
|
||||
|
||||
/// Creates a new [`DynamicArray`] from this array.
|
||||
fn to_dynamic_array(&self) -> DynamicArray {
|
||||
DynamicArray {
|
||||
represented_type: self.get_represented_type_info(),
|
||||
values: self.iter().map(PartialReflect::clone_value).collect(),
|
||||
values: self.iter().map(PartialReflect::to_dynamic).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,11 +262,6 @@ impl PartialReflect for DynamicArray {
|
||||
ReflectOwned::Array(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_hash(&self) -> Option<u64> {
|
||||
array_hash(self)
|
||||
@ -307,18 +308,6 @@ impl Array for DynamicArray {
|
||||
fn drain(self: Box<Self>) -> Vec<Box<dyn PartialReflect>> {
|
||||
self.values.into_vec()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_dynamic(&self) -> DynamicArray {
|
||||
DynamicArray {
|
||||
represented_type: self.represented_type,
|
||||
values: self
|
||||
.values
|
||||
.iter()
|
||||
.map(|value| value.clone_value())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Box<dyn PartialReflect>> for DynamicArray {
|
||||
|
@ -23,8 +23,8 @@ impl Clone for DynamicVariant {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
DynamicVariant::Unit => DynamicVariant::Unit,
|
||||
DynamicVariant::Tuple(data) => DynamicVariant::Tuple(data.clone_dynamic()),
|
||||
DynamicVariant::Struct(data) => DynamicVariant::Struct(data.clone_dynamic()),
|
||||
DynamicVariant::Tuple(data) => DynamicVariant::Tuple(data.to_dynamic_tuple()),
|
||||
DynamicVariant::Struct(data) => DynamicVariant::Struct(data.to_dynamic_struct()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -150,7 +150,7 @@ impl DynamicEnum {
|
||||
/// Create a [`DynamicEnum`] from an existing one.
|
||||
///
|
||||
/// This is functionally the same as [`DynamicEnum::from`] except it takes a reference.
|
||||
pub fn from_ref<TEnum: Enum>(value: &TEnum) -> Self {
|
||||
pub fn from_ref<TEnum: Enum + ?Sized>(value: &TEnum) -> Self {
|
||||
let type_info = value.get_represented_type_info();
|
||||
let mut dyn_enum = match value.variant_type() {
|
||||
VariantType::Unit => DynamicEnum::new_with_index(
|
||||
@ -161,7 +161,7 @@ impl DynamicEnum {
|
||||
VariantType::Tuple => {
|
||||
let mut data = DynamicTuple::default();
|
||||
for field in value.iter_fields() {
|
||||
data.insert_boxed(field.value().clone_value());
|
||||
data.insert_boxed(field.value().to_dynamic());
|
||||
}
|
||||
DynamicEnum::new_with_index(
|
||||
value.variant_index(),
|
||||
@ -173,7 +173,7 @@ impl DynamicEnum {
|
||||
let mut data = DynamicStruct::default();
|
||||
for field in value.iter_fields() {
|
||||
let name = field.name().unwrap();
|
||||
data.insert_boxed(name, field.value().clone_value());
|
||||
data.insert_boxed(name, field.value().to_dynamic());
|
||||
}
|
||||
DynamicEnum::new_with_index(
|
||||
value.variant_index(),
|
||||
@ -339,14 +339,14 @@ impl PartialReflect for DynamicEnum {
|
||||
VariantType::Tuple => {
|
||||
let mut dyn_tuple = DynamicTuple::default();
|
||||
for field in value.iter_fields() {
|
||||
dyn_tuple.insert_boxed(field.value().clone_value());
|
||||
dyn_tuple.insert_boxed(field.value().to_dynamic());
|
||||
}
|
||||
DynamicVariant::Tuple(dyn_tuple)
|
||||
}
|
||||
VariantType::Struct => {
|
||||
let mut dyn_struct = DynamicStruct::default();
|
||||
for field in value.iter_fields() {
|
||||
dyn_struct.insert_boxed(field.name().unwrap(), field.value().clone_value());
|
||||
dyn_struct.insert_boxed(field.name().unwrap(), field.value().to_dynamic());
|
||||
}
|
||||
DynamicVariant::Struct(dyn_struct)
|
||||
}
|
||||
@ -377,11 +377,6 @@ impl PartialReflect for DynamicEnum {
|
||||
ReflectOwned::Enum(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_hash(&self) -> Option<u64> {
|
||||
enum_hash(self)
|
||||
|
@ -125,7 +125,14 @@ pub trait Enum: PartialReflect {
|
||||
/// The type of the current variant.
|
||||
fn variant_type(&self) -> VariantType;
|
||||
// Clones the enum into a [`DynamicEnum`].
|
||||
fn clone_dynamic(&self) -> DynamicEnum;
|
||||
#[deprecated(since = "0.16.0", note = "use `to_dynamic_enum` instead")]
|
||||
fn clone_dynamic(&self) -> DynamicEnum {
|
||||
self.to_dynamic_enum()
|
||||
}
|
||||
/// Creates a new [`DynamicEnum`] from this enum.
|
||||
fn to_dynamic_enum(&self) -> DynamicEnum {
|
||||
DynamicEnum::from_ref(self)
|
||||
}
|
||||
/// Returns true if the current variant's type matches the given one.
|
||||
fn is_variant(&self, variant_type: VariantType) -> bool {
|
||||
self.variant_type() == variant_type
|
||||
|
@ -358,7 +358,7 @@ impl Function for DynamicFunction<'static> {
|
||||
self.call(args)
|
||||
}
|
||||
|
||||
fn clone_dynamic(&self) -> DynamicFunction<'static> {
|
||||
fn to_dynamic_function(&self) -> DynamicFunction<'static> {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
@ -395,7 +395,7 @@ impl PartialReflect for DynamicFunction<'static> {
|
||||
fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> {
|
||||
match value.reflect_ref() {
|
||||
ReflectRef::Function(func) => {
|
||||
*self = func.clone_dynamic();
|
||||
*self = func.to_dynamic_function();
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(ApplyError::MismatchedTypes {
|
||||
@ -421,10 +421,6 @@ impl PartialReflect for DynamicFunction<'static> {
|
||||
ReflectOwned::Function(self)
|
||||
}
|
||||
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn reflect_hash(&self) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
@ -562,14 +558,14 @@ mod tests {
|
||||
assert_eq!(greet.name().unwrap(), "greet");
|
||||
assert_eq!(clone.name().unwrap(), "greet");
|
||||
|
||||
let clone_value = clone
|
||||
let cloned_value = clone
|
||||
.call(ArgList::default().with_ref(&String::from("world")))
|
||||
.unwrap()
|
||||
.unwrap_owned()
|
||||
.try_take::<String>()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(clone_value, "Hello, world!");
|
||||
assert_eq!(cloned_value, "Hello, world!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -64,7 +64,13 @@ pub trait Function: PartialReflect + Debug {
|
||||
fn reflect_call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a>;
|
||||
|
||||
/// Clone this function into a [`DynamicFunction`].
|
||||
fn clone_dynamic(&self) -> DynamicFunction<'static>;
|
||||
#[deprecated(since = "0.16.0", note = "use `to_dynamic_function` instead")]
|
||||
fn clone_dynamic(&self) -> DynamicFunction<'static> {
|
||||
self.to_dynamic_function()
|
||||
}
|
||||
|
||||
/// Creates a new [`DynamicFunction`] from this function.
|
||||
fn to_dynamic_function(&self) -> DynamicFunction<'static>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -134,10 +134,6 @@ where
|
||||
ReflectOwned::List(self)
|
||||
}
|
||||
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
||||
Ok(Box::new(
|
||||
self.iter()
|
||||
|
@ -9,12 +9,11 @@ use crate::{
|
||||
reflect::impl_full_reflect,
|
||||
set_apply, set_partial_eq, set_try_apply,
|
||||
utility::{reflect_hasher, GenericTypeInfoCell, GenericTypePathCell, NonGenericTypeInfoCell},
|
||||
ApplyError, Array, ArrayInfo, ArrayIter, DynamicMap, DynamicSet, DynamicTypePath, FromReflect,
|
||||
FromType, Generics, GetTypeRegistration, List, ListInfo, ListIter, Map, MapInfo, MapIter,
|
||||
MaybeTyped, OpaqueInfo, PartialReflect, Reflect, ReflectCloneError, ReflectDeserialize,
|
||||
ReflectFromPtr, ReflectFromReflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef,
|
||||
ReflectSerialize, Set, SetInfo, TypeInfo, TypeParamInfo, TypePath, TypeRegistration,
|
||||
TypeRegistry, Typed,
|
||||
ApplyError, Array, ArrayInfo, ArrayIter, DynamicMap, DynamicTypePath, FromReflect, FromType,
|
||||
Generics, GetTypeRegistration, List, ListInfo, ListIter, Map, MapInfo, MapIter, MaybeTyped,
|
||||
OpaqueInfo, PartialReflect, Reflect, ReflectCloneError, ReflectDeserialize, ReflectFromPtr,
|
||||
ReflectFromReflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, ReflectSerialize, Set,
|
||||
SetInfo, TypeInfo, TypeParamInfo, TypePath, TypeRegistration, TypeRegistry, Typed,
|
||||
};
|
||||
use alloc::{
|
||||
borrow::{Cow, ToOwned},
|
||||
@ -409,10 +408,6 @@ macro_rules! impl_reflect_for_atomic {
|
||||
fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> {
|
||||
Some(self)
|
||||
}
|
||||
#[inline]
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(<$ty>::new(self.load($ordering)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
||||
@ -626,10 +621,6 @@ macro_rules! impl_reflect_for_veclike {
|
||||
ReflectOwned::List(self)
|
||||
}
|
||||
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
||||
Ok(Box::new(
|
||||
self.iter()
|
||||
@ -779,7 +770,7 @@ macro_rules! impl_reflect_for_hashmap {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn clone_dynamic(&self) -> DynamicMap {
|
||||
fn to_dynamic_map(&self) -> DynamicMap {
|
||||
let mut dynamic_map = DynamicMap::default();
|
||||
dynamic_map.set_represented_type(self.get_represented_type_info());
|
||||
for (k, v) in self {
|
||||
@ -789,7 +780,7 @@ macro_rules! impl_reflect_for_hashmap {
|
||||
k.reflect_type_path()
|
||||
)
|
||||
});
|
||||
dynamic_map.insert_boxed(Box::new(key), v.clone_value());
|
||||
dynamic_map.insert_boxed(Box::new(key), v.to_dynamic());
|
||||
}
|
||||
dynamic_map
|
||||
}
|
||||
@ -880,10 +871,6 @@ macro_rules! impl_reflect_for_hashmap {
|
||||
ReflectOwned::Map(self)
|
||||
}
|
||||
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
||||
let mut map = Self::with_capacity_and_hasher(self.len(), S::default());
|
||||
for (key, value) in self.iter() {
|
||||
@ -1043,15 +1030,6 @@ macro_rules! impl_reflect_for_hashset {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn clone_dynamic(&self) -> DynamicSet {
|
||||
let mut dynamic_set = DynamicSet::default();
|
||||
dynamic_set.set_represented_type(self.get_represented_type_info());
|
||||
for v in self {
|
||||
dynamic_set.insert_boxed(v.clone_value());
|
||||
}
|
||||
dynamic_set
|
||||
}
|
||||
|
||||
fn insert_boxed(&mut self, value: Box<dyn PartialReflect>) -> bool {
|
||||
let value = V::take_from_reflect(value).unwrap_or_else(|value| {
|
||||
panic!(
|
||||
@ -1146,10 +1124,6 @@ macro_rules! impl_reflect_for_hashset {
|
||||
ReflectOwned::Set(self)
|
||||
}
|
||||
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
||||
let mut set = Self::with_capacity_and_hasher(self.len(), S::default());
|
||||
for value in self.iter() {
|
||||
@ -1321,7 +1295,7 @@ where
|
||||
k.reflect_type_path()
|
||||
)
|
||||
});
|
||||
dynamic_map.insert_boxed(Box::new(key), v.clone_value());
|
||||
dynamic_map.insert_boxed(Box::new(key), v.to_dynamic());
|
||||
}
|
||||
dynamic_map
|
||||
}
|
||||
@ -1407,10 +1381,6 @@ where
|
||||
ReflectOwned::Map(self)
|
||||
}
|
||||
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
||||
let mut map = Self::new();
|
||||
for (key, value) in self.iter() {
|
||||
@ -1596,11 +1566,6 @@ impl<T: Reflect + MaybeTyped + TypePath + GetTypeRegistration, const N: usize> P
|
||||
ReflectOwned::Array(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_hash(&self) -> Option<u64> {
|
||||
crate::array_hash(self)
|
||||
@ -1795,10 +1760,6 @@ impl PartialReflect for Cow<'static, str> {
|
||||
ReflectOwned::Opaque(self)
|
||||
}
|
||||
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
||||
Ok(Box::new(self.clone()))
|
||||
}
|
||||
@ -1987,10 +1948,6 @@ impl<T: FromReflect + MaybeTyped + Clone + TypePath + GetTypeRegistration> Parti
|
||||
ReflectOwned::List(self)
|
||||
}
|
||||
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(List::clone_dynamic(self))
|
||||
}
|
||||
|
||||
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
||||
Ok(Box::new(self.clone()))
|
||||
}
|
||||
@ -2100,10 +2057,6 @@ impl PartialReflect for &'static str {
|
||||
ReflectOwned::Opaque(self)
|
||||
}
|
||||
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(*self)
|
||||
}
|
||||
|
||||
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
||||
Ok(Box::new(*self))
|
||||
}
|
||||
@ -2243,10 +2196,6 @@ impl PartialReflect for &'static Path {
|
||||
ReflectOwned::Opaque(self)
|
||||
}
|
||||
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(*self)
|
||||
}
|
||||
|
||||
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
||||
Ok(Box::new(*self))
|
||||
}
|
||||
@ -2386,10 +2335,6 @@ impl PartialReflect for Cow<'static, Path> {
|
||||
ReflectOwned::Opaque(self)
|
||||
}
|
||||
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
||||
Ok(Box::new(self.clone()))
|
||||
}
|
||||
@ -2548,10 +2493,6 @@ impl PartialReflect for &'static Location<'static> {
|
||||
ReflectOwned::Opaque(self)
|
||||
}
|
||||
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(*self)
|
||||
}
|
||||
|
||||
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
||||
Ok(Box::new(*self))
|
||||
}
|
||||
|
@ -204,8 +204,8 @@
|
||||
//!
|
||||
//! They are most commonly used as "proxies" for other types,
|
||||
//! where they contain the same data as— and therefore, represent— a concrete type.
|
||||
//! The [`PartialReflect::clone_value`] method will return a dynamic type for all non-opaque types,
|
||||
//! allowing all types to essentially be "cloned".
|
||||
//! The [`PartialReflect::to_dynamic`] method will return a dynamic type for all non-opaque types,
|
||||
//! allowing all types to essentially be "cloned" into a dynamic type.
|
||||
//! And since dynamic types themselves implement [`PartialReflect`],
|
||||
//! we may pass them around just like most other reflected types.
|
||||
//!
|
||||
@ -219,9 +219,9 @@
|
||||
//! foo: 123
|
||||
//! });
|
||||
//!
|
||||
//! // `cloned` will be a `DynamicStruct` representing a `MyStruct`
|
||||
//! let cloned: Box<dyn PartialReflect> = original.clone_value();
|
||||
//! assert!(cloned.represents::<MyStruct>());
|
||||
//! // `dynamic` will be a `DynamicStruct` representing a `MyStruct`
|
||||
//! let dynamic: Box<dyn PartialReflect> = original.to_dynamic();
|
||||
//! assert!(dynamic.represents::<MyStruct>());
|
||||
//! ```
|
||||
//!
|
||||
//! ## Patching
|
||||
@ -253,8 +253,8 @@
|
||||
//! foo: 123
|
||||
//! });
|
||||
//!
|
||||
//! let cloned: Box<dyn PartialReflect> = original.clone_value();
|
||||
//! let value = cloned.try_take::<MyStruct>().unwrap(); // PANIC!
|
||||
//! let dynamic: Box<dyn PartialReflect> = original.to_dynamic();
|
||||
//! let value = dynamic.try_take::<MyStruct>().unwrap(); // PANIC!
|
||||
//! ```
|
||||
//!
|
||||
//! To resolve this issue, we'll need to convert the dynamic type to the concrete one.
|
||||
@ -278,8 +278,8 @@
|
||||
//! foo: 123
|
||||
//! });
|
||||
//!
|
||||
//! let cloned: Box<dyn PartialReflect> = original.clone_value();
|
||||
//! let value = <MyStruct as FromReflect>::from_reflect(&*cloned).unwrap(); // OK!
|
||||
//! let dynamic: Box<dyn PartialReflect> = original.to_dynamic();
|
||||
//! let value = <MyStruct as FromReflect>::from_reflect(&*dynamic).unwrap(); // OK!
|
||||
//! ```
|
||||
//!
|
||||
//! When deriving, all active fields and sub-elements must also implement `FromReflect`.
|
||||
@ -945,7 +945,7 @@ mod tests {
|
||||
|
||||
let foo = Foo { a: 1 };
|
||||
assert!(foo.reflect_hash().is_some());
|
||||
let dynamic = foo.clone_dynamic();
|
||||
let dynamic = foo.to_dynamic_struct();
|
||||
|
||||
let mut map = DynamicMap::default();
|
||||
map.insert(dynamic, 11u32);
|
||||
@ -1498,7 +1498,7 @@ mod tests {
|
||||
list.push(3isize);
|
||||
list.push(4isize);
|
||||
list.push(5isize);
|
||||
foo_patch.insert("c", list.clone_dynamic());
|
||||
foo_patch.insert("c", list.to_dynamic_list());
|
||||
|
||||
let mut map = DynamicMap::default();
|
||||
map.insert(2usize, 3i8);
|
||||
@ -1507,7 +1507,7 @@ mod tests {
|
||||
|
||||
let mut bar_patch = DynamicStruct::default();
|
||||
bar_patch.insert("x", 2u32);
|
||||
foo_patch.insert("e", bar_patch.clone_dynamic());
|
||||
foo_patch.insert("e", bar_patch.to_dynamic_struct());
|
||||
|
||||
let mut tuple = DynamicTuple::default();
|
||||
tuple.insert(2i32);
|
||||
@ -1834,22 +1834,22 @@ mod tests {
|
||||
#[test]
|
||||
fn not_dynamic_names() {
|
||||
let list = Vec::<usize>::new();
|
||||
let dyn_list = list.clone_dynamic();
|
||||
let dyn_list = list.to_dynamic_list();
|
||||
assert_ne!(dyn_list.reflect_type_path(), Vec::<usize>::type_path());
|
||||
|
||||
let array = [b'0'; 4];
|
||||
let dyn_array = array.clone_dynamic();
|
||||
let dyn_array = array.to_dynamic_array();
|
||||
assert_ne!(dyn_array.reflect_type_path(), <[u8; 4]>::type_path());
|
||||
|
||||
let map = HashMap::<usize, String>::default();
|
||||
let dyn_map = map.clone_dynamic();
|
||||
let dyn_map = map.to_dynamic_map();
|
||||
assert_ne!(
|
||||
dyn_map.reflect_type_path(),
|
||||
HashMap::<usize, String>::type_path()
|
||||
);
|
||||
|
||||
let tuple = (0usize, "1".to_string(), 2.0f32);
|
||||
let mut dyn_tuple = tuple.clone_dynamic();
|
||||
let mut dyn_tuple = tuple.to_dynamic_tuple();
|
||||
dyn_tuple.insert::<usize>(3);
|
||||
assert_ne!(
|
||||
dyn_tuple.reflect_type_path(),
|
||||
@ -1861,13 +1861,13 @@ mod tests {
|
||||
a: usize,
|
||||
}
|
||||
let struct_ = TestStruct { a: 0 };
|
||||
let dyn_struct = struct_.clone_dynamic();
|
||||
let dyn_struct = struct_.to_dynamic_struct();
|
||||
assert_ne!(dyn_struct.reflect_type_path(), TestStruct::type_path());
|
||||
|
||||
#[derive(Reflect)]
|
||||
struct TestTupleStruct(usize);
|
||||
let tuple_struct = TestTupleStruct(0);
|
||||
let dyn_tuple_struct = tuple_struct.clone_dynamic();
|
||||
let dyn_tuple_struct = tuple_struct.to_dynamic_tuple_struct();
|
||||
assert_ne!(
|
||||
dyn_tuple_struct.reflect_type_path(),
|
||||
TestTupleStruct::type_path()
|
||||
@ -2252,7 +2252,7 @@ mod tests {
|
||||
#[test]
|
||||
fn should_permit_valid_represented_type_for_dynamic() {
|
||||
let type_info = <[i32; 2] as Typed>::type_info();
|
||||
let mut dynamic_array = [123; 2].clone_dynamic();
|
||||
let mut dynamic_array = [123; 2].to_dynamic_array();
|
||||
dynamic_array.set_represented_type(Some(type_info));
|
||||
}
|
||||
|
||||
@ -2260,7 +2260,7 @@ mod tests {
|
||||
#[should_panic(expected = "expected TypeInfo::Array but received")]
|
||||
fn should_prohibit_invalid_represented_type_for_dynamic() {
|
||||
let type_info = <(i32, i32) as Typed>::type_info();
|
||||
let mut dynamic_array = [123; 2].clone_dynamic();
|
||||
let mut dynamic_array = [123; 2].to_dynamic_array();
|
||||
dynamic_array.set_represented_type(Some(type_info));
|
||||
}
|
||||
|
||||
@ -2800,7 +2800,7 @@ bevy_reflect::tests::Test {
|
||||
map,
|
||||
value: 12,
|
||||
}
|
||||
.clone_dynamic();
|
||||
.to_dynamic_struct();
|
||||
|
||||
// test unknown DynamicStruct
|
||||
let mut test_unknown_struct = DynamicStruct::default();
|
||||
|
@ -104,10 +104,16 @@ pub trait List: PartialReflect {
|
||||
fn drain(&mut self) -> Vec<Box<dyn PartialReflect>>;
|
||||
|
||||
/// Clones the list, producing a [`DynamicList`].
|
||||
#[deprecated(since = "0.16.0", note = "use `to_dynamic_list` instead")]
|
||||
fn clone_dynamic(&self) -> DynamicList {
|
||||
self.to_dynamic_list()
|
||||
}
|
||||
|
||||
/// Creates a new [`DynamicList`] from this list.
|
||||
fn to_dynamic_list(&self) -> DynamicList {
|
||||
DynamicList {
|
||||
represented_type: self.get_represented_type_info(),
|
||||
values: self.iter().map(PartialReflect::clone_value).collect(),
|
||||
values: self.iter().map(PartialReflect::to_dynamic).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,17 +252,6 @@ impl List for DynamicList {
|
||||
fn drain(&mut self) -> Vec<Box<dyn PartialReflect>> {
|
||||
self.values.drain(..).collect()
|
||||
}
|
||||
|
||||
fn clone_dynamic(&self) -> DynamicList {
|
||||
DynamicList {
|
||||
represented_type: self.represented_type,
|
||||
values: self
|
||||
.values
|
||||
.iter()
|
||||
.map(|value| value.clone_value())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialReflect for DynamicList {
|
||||
@ -320,11 +315,6 @@ impl PartialReflect for DynamicList {
|
||||
ReflectOwned::List(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_hash(&self) -> Option<u64> {
|
||||
list_hash(self)
|
||||
@ -470,7 +460,7 @@ pub fn list_try_apply<L: List>(a: &mut L, b: &dyn PartialReflect) -> Result<(),
|
||||
v.try_apply(value)?;
|
||||
}
|
||||
} else {
|
||||
List::push(a, value.clone_value());
|
||||
List::push(a, value.to_dynamic());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,20 @@ pub trait Map: PartialReflect {
|
||||
fn drain(&mut self) -> Vec<(Box<dyn PartialReflect>, Box<dyn PartialReflect>)>;
|
||||
|
||||
/// Clones the map, producing a [`DynamicMap`].
|
||||
fn clone_dynamic(&self) -> DynamicMap;
|
||||
#[deprecated(since = "0.16.0", note = "use `to_dynamic_map` instead")]
|
||||
fn clone_dynamic(&self) -> DynamicMap {
|
||||
self.to_dynamic_map()
|
||||
}
|
||||
|
||||
/// Creates a new [`DynamicMap`] from this map.
|
||||
fn to_dynamic_map(&self) -> DynamicMap {
|
||||
let mut map = DynamicMap::default();
|
||||
map.set_represented_type(self.get_represented_type_info());
|
||||
for (key, value) in self.iter() {
|
||||
map.insert_boxed(key.to_dynamic(), value.to_dynamic());
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
/// Inserts a key-value pair into the map.
|
||||
///
|
||||
@ -302,18 +315,6 @@ impl Map for DynamicMap {
|
||||
self.values.drain(..).collect()
|
||||
}
|
||||
|
||||
fn clone_dynamic(&self) -> DynamicMap {
|
||||
DynamicMap {
|
||||
represented_type: self.represented_type,
|
||||
values: self
|
||||
.values
|
||||
.iter()
|
||||
.map(|(key, value)| (key.clone_value(), value.clone_value()))
|
||||
.collect(),
|
||||
indices: self.indices.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_boxed(
|
||||
&mut self,
|
||||
key: Box<dyn PartialReflect>,
|
||||
@ -431,10 +432,6 @@ impl PartialReflect for DynamicMap {
|
||||
ReflectOwned::Map(self)
|
||||
}
|
||||
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option<bool> {
|
||||
map_partial_eq(self, value)
|
||||
}
|
||||
@ -620,7 +617,7 @@ pub fn map_try_apply<M: Map>(a: &mut M, b: &dyn PartialReflect) -> Result<(), Ap
|
||||
if let Some(a_value) = a.get_mut(key) {
|
||||
a_value.try_apply(b_value)?;
|
||||
} else {
|
||||
a.insert_boxed(key.clone_value(), b_value.clone_value());
|
||||
a.insert_boxed(key.to_dynamic(), b_value.to_dynamic());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,11 +246,69 @@ where
|
||||
/// [`Struct`]: crate::Struct
|
||||
/// [`Struct::clone_dynamic`]: crate::Struct::clone_dynamic
|
||||
/// [`DynamicStruct`]: crate::DynamicStruct
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect>;
|
||||
#[deprecated(
|
||||
since = "0.16.0",
|
||||
note = "to clone reflected values, prefer using `reflect_clone`. To convert reflected values to dynamic ones, use `to_dynamic`."
|
||||
)]
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
self.to_dynamic()
|
||||
}
|
||||
|
||||
/// Converts this reflected value into its dynamic representation based on its [kind].
|
||||
///
|
||||
/// For example, a [`List`] type will internally invoke [`List::to_dynamic_list`], returning [`DynamicList`].
|
||||
/// A [`Struct`] type will invoke [`Struct::to_dynamic_struct`], returning [`DynamicStruct`].
|
||||
/// And so on.
|
||||
///
|
||||
/// If the [kind] is [opaque], then the value will attempt to be cloned directly via [`reflect_clone`],
|
||||
/// since opaque types do not have any standard dynamic representation.
|
||||
///
|
||||
/// To attempt to clone the value directly such that it returns a concrete instance of this type,
|
||||
/// use [`reflect_clone`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if the [kind] is [opaque] and the call to [`reflect_clone`] fails.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::{PartialReflect};
|
||||
/// let value = (1, true, 3.14);
|
||||
/// let dynamic_value = value.to_dynamic();
|
||||
/// assert!(dynamic_value.is_dynamic())
|
||||
/// ```
|
||||
///
|
||||
/// [kind]: PartialReflect::reflect_kind
|
||||
/// [`List`]: crate::List
|
||||
/// [`List::to_dynamic_list`]: crate::List::to_dynamic_list
|
||||
/// [`DynamicList`]: crate::DynamicList
|
||||
/// [`Struct`]: crate::Struct
|
||||
/// [`Struct::to_dynamic_struct`]: crate::Struct::to_dynamic_struct
|
||||
/// [`DynamicStruct`]: crate::DynamicStruct
|
||||
/// [opaque]: crate::ReflectKind::Opaque
|
||||
/// [`reflect_clone`]: PartialReflect::reflect_clone
|
||||
fn to_dynamic(&self) -> Box<dyn PartialReflect> {
|
||||
match self.reflect_ref() {
|
||||
ReflectRef::Struct(dyn_struct) => Box::new(dyn_struct.to_dynamic_struct()),
|
||||
ReflectRef::TupleStruct(dyn_tuple_struct) => {
|
||||
Box::new(dyn_tuple_struct.to_dynamic_tuple_struct())
|
||||
}
|
||||
ReflectRef::Tuple(dyn_tuple) => Box::new(dyn_tuple.to_dynamic_tuple()),
|
||||
ReflectRef::List(dyn_list) => Box::new(dyn_list.to_dynamic_list()),
|
||||
ReflectRef::Array(dyn_array) => Box::new(dyn_array.to_dynamic_array()),
|
||||
ReflectRef::Map(dyn_map) => Box::new(dyn_map.to_dynamic_map()),
|
||||
ReflectRef::Set(dyn_set) => Box::new(dyn_set.to_dynamic_set()),
|
||||
ReflectRef::Enum(dyn_enum) => Box::new(dyn_enum.to_dynamic_enum()),
|
||||
#[cfg(feature = "functions")]
|
||||
ReflectRef::Function(dyn_function) => Box::new(dyn_function.to_dynamic_function()),
|
||||
ReflectRef::Opaque(value) => value.reflect_clone().unwrap().into_partial_reflect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to clone `Self` using reflection.
|
||||
///
|
||||
/// Unlike [`PartialReflect::clone_value`], which often returns a dynamic representation of `Self`,
|
||||
/// Unlike [`to_dynamic`], which generally returns a dynamic representation of `Self`,
|
||||
/// this method attempts create a clone of `Self` directly, if possible.
|
||||
///
|
||||
/// If the clone cannot be performed, an appropriate [`ReflectCloneError`] is returned.
|
||||
@ -263,6 +321,8 @@ where
|
||||
/// let cloned = value.reflect_clone().unwrap();
|
||||
/// assert!(cloned.is::<(i32, bool, f64)>())
|
||||
/// ```
|
||||
///
|
||||
/// [`to_dynamic`]: PartialReflect::to_dynamic
|
||||
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError> {
|
||||
Err(ReflectCloneError::NotImplemented {
|
||||
type_path: Cow::Owned(self.reflect_type_path().to_string()),
|
||||
|
@ -164,7 +164,7 @@ mod tests {
|
||||
let mut registry = TypeRegistry::default();
|
||||
registry.register::<TestStruct>();
|
||||
|
||||
let value: DynamicStruct = TestStruct { a: 123, b: 456 }.clone_dynamic();
|
||||
let value: DynamicStruct = TestStruct { a: 123, b: 456 }.to_dynamic_struct();
|
||||
|
||||
let serializer = ReflectSerializer::new(&value, ®istry);
|
||||
|
||||
@ -175,7 +175,7 @@ mod tests {
|
||||
let mut deserializer = ron::de::Deserializer::from_str(&result).unwrap();
|
||||
let reflect_deserializer = ReflectDeserializer::new(®istry);
|
||||
|
||||
let expected = value.clone_value();
|
||||
let expected = value.to_dynamic();
|
||||
let result = reflect_deserializer.deserialize(&mut deserializer).unwrap();
|
||||
|
||||
assert!(expected
|
||||
|
@ -406,7 +406,7 @@ mod tests {
|
||||
some: Some(SomeStruct { foo: 999999999 }),
|
||||
none: None,
|
||||
};
|
||||
let dynamic = value.clone_dynamic();
|
||||
let dynamic = value.to_dynamic_struct();
|
||||
let reflect = dynamic.as_partial_reflect();
|
||||
|
||||
let registry = get_registry();
|
||||
|
@ -70,7 +70,20 @@ pub trait Set: PartialReflect {
|
||||
fn drain(&mut self) -> Vec<Box<dyn PartialReflect>>;
|
||||
|
||||
/// Clones the set, producing a [`DynamicSet`].
|
||||
fn clone_dynamic(&self) -> DynamicSet;
|
||||
#[deprecated(since = "0.16.0", note = "use `to_dynamic_set` instead")]
|
||||
fn clone_dynamic(&self) -> DynamicSet {
|
||||
self.to_dynamic_set()
|
||||
}
|
||||
|
||||
/// Creates a new [`DynamicSet`] from this set.
|
||||
fn to_dynamic_set(&self) -> DynamicSet {
|
||||
let mut set = DynamicSet::default();
|
||||
set.set_represented_type(self.get_represented_type_info());
|
||||
for value in self.iter() {
|
||||
set.insert_boxed(value.to_dynamic());
|
||||
}
|
||||
set
|
||||
}
|
||||
|
||||
/// Inserts a value into the set.
|
||||
///
|
||||
@ -201,23 +214,6 @@ impl Set for DynamicSet {
|
||||
self.hash_table.drain().collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn clone_dynamic(&self) -> DynamicSet {
|
||||
let mut hash_table = HashTable::new();
|
||||
self.hash_table
|
||||
.iter()
|
||||
.map(|value| value.clone_value())
|
||||
.for_each(|value| {
|
||||
hash_table.insert_unique(Self::internal_hash(value.as_ref()), value, |boxed| {
|
||||
Self::internal_hash(boxed.as_ref())
|
||||
});
|
||||
});
|
||||
|
||||
DynamicSet {
|
||||
represented_type: self.represented_type,
|
||||
hash_table,
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_boxed(&mut self, value: Box<dyn PartialReflect>) -> bool {
|
||||
assert_eq!(
|
||||
value.reflect_partial_eq(&*value),
|
||||
@ -317,10 +313,6 @@ impl PartialReflect for DynamicSet {
|
||||
ReflectOwned::Set(self)
|
||||
}
|
||||
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option<bool> {
|
||||
set_partial_eq(self, value)
|
||||
}
|
||||
@ -467,7 +459,7 @@ pub fn set_apply<M: Set>(a: &mut M, b: &dyn PartialReflect) {
|
||||
if let ReflectRef::Set(set_value) = b.reflect_ref() {
|
||||
for b_value in set_value.iter() {
|
||||
if a.get(b_value).is_none() {
|
||||
a.insert_boxed(b_value.clone_value());
|
||||
a.insert_boxed(b_value.to_dynamic());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -490,7 +482,7 @@ pub fn set_try_apply<S: Set>(a: &mut S, b: &dyn PartialReflect) -> Result<(), Ap
|
||||
|
||||
for b_value in set_value.iter() {
|
||||
if a.get(b_value).is_none() {
|
||||
a.insert_boxed(b_value.clone_value());
|
||||
a.insert_boxed(b_value.to_dynamic());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,19 @@ pub trait Struct: PartialReflect {
|
||||
fn iter_fields(&self) -> FieldIter;
|
||||
|
||||
/// Clones the struct into a [`DynamicStruct`].
|
||||
fn clone_dynamic(&self) -> DynamicStruct;
|
||||
#[deprecated(since = "0.16.0", note = "use `to_dynamic_struct` instead")]
|
||||
fn clone_dynamic(&self) -> DynamicStruct {
|
||||
self.to_dynamic_struct()
|
||||
}
|
||||
|
||||
fn to_dynamic_struct(&self) -> DynamicStruct {
|
||||
let mut dynamic_struct = DynamicStruct::default();
|
||||
dynamic_struct.set_represented_type(self.get_represented_type_info());
|
||||
for (i, value) in self.iter_fields().enumerate() {
|
||||
dynamic_struct.insert_boxed(self.name_at(i).unwrap(), value.to_dynamic());
|
||||
}
|
||||
dynamic_struct
|
||||
}
|
||||
|
||||
/// Will return `None` if [`TypeInfo`] is not available.
|
||||
fn get_represented_struct_info(&self) -> Option<&'static StructInfo> {
|
||||
@ -370,19 +382,6 @@ impl Struct for DynamicStruct {
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn clone_dynamic(&self) -> DynamicStruct {
|
||||
DynamicStruct {
|
||||
represented_type: self.get_represented_type_info(),
|
||||
field_names: self.field_names.clone(),
|
||||
field_indices: self.field_indices.clone(),
|
||||
fields: self
|
||||
.fields
|
||||
.iter()
|
||||
.map(|value| value.clone_value())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialReflect for DynamicStruct {
|
||||
@ -449,11 +448,6 @@ impl PartialReflect for DynamicStruct {
|
||||
ReflectOwned::Struct(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option<bool> {
|
||||
struct_partial_eq(self, value)
|
||||
}
|
||||
|
@ -55,8 +55,19 @@ pub trait Tuple: PartialReflect {
|
||||
/// Drain the fields of this tuple to get a vector of owned values.
|
||||
fn drain(self: Box<Self>) -> Vec<Box<dyn PartialReflect>>;
|
||||
|
||||
/// Clones the struct into a [`DynamicTuple`].
|
||||
fn clone_dynamic(&self) -> DynamicTuple;
|
||||
/// Clones the tuple into a [`DynamicTuple`].
|
||||
#[deprecated(since = "0.16.0", note = "use `to_dynamic_tuple` instead")]
|
||||
fn clone_dynamic(&self) -> DynamicTuple {
|
||||
self.to_dynamic_tuple()
|
||||
}
|
||||
|
||||
/// Creates a new [`DynamicTuple`] from this tuple.
|
||||
fn to_dynamic_tuple(&self) -> DynamicTuple {
|
||||
DynamicTuple {
|
||||
represented_type: self.get_represented_type_info(),
|
||||
fields: self.iter_fields().map(PartialReflect::to_dynamic).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Will return `None` if [`TypeInfo`] is not available.
|
||||
fn get_represented_tuple_info(&self) -> Option<&'static TupleInfo> {
|
||||
@ -270,18 +281,6 @@ impl Tuple for DynamicTuple {
|
||||
fn drain(self: Box<Self>) -> Vec<Box<dyn PartialReflect>> {
|
||||
self.fields
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_dynamic(&self) -> DynamicTuple {
|
||||
DynamicTuple {
|
||||
represented_type: self.represented_type,
|
||||
fields: self
|
||||
.fields
|
||||
.iter()
|
||||
.map(|value| value.clone_value())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialReflect for DynamicTuple {
|
||||
@ -339,11 +338,6 @@ impl PartialReflect for DynamicTuple {
|
||||
ReflectOwned::Tuple(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> {
|
||||
tuple_try_apply(self, value)
|
||||
}
|
||||
@ -518,18 +512,6 @@ macro_rules! impl_reflect_tuple {
|
||||
$(Box::new(self.$index),)*
|
||||
]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_dynamic(&self) -> DynamicTuple {
|
||||
let info = self.get_represented_type_info();
|
||||
DynamicTuple {
|
||||
represented_type: info,
|
||||
fields: self
|
||||
.iter_fields()
|
||||
.map(|value| value.clone_value())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($name: Reflect + MaybeTyped + TypePath + GetTypeRegistration),*> PartialReflect for ($($name,)*) {
|
||||
@ -578,10 +560,6 @@ macro_rules! impl_reflect_tuple {
|
||||
ReflectOwned::Tuple(self)
|
||||
}
|
||||
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option<bool> {
|
||||
crate::tuple_partial_eq(self, value)
|
||||
}
|
||||
|
@ -56,7 +56,18 @@ pub trait TupleStruct: PartialReflect {
|
||||
fn iter_fields(&self) -> TupleStructFieldIter;
|
||||
|
||||
/// Clones the struct into a [`DynamicTupleStruct`].
|
||||
fn clone_dynamic(&self) -> DynamicTupleStruct;
|
||||
#[deprecated(since = "0.16.0", note = "use `to_dynamic_tuple_struct` instead")]
|
||||
fn clone_dynamic(&self) -> DynamicTupleStruct {
|
||||
self.to_dynamic_tuple_struct()
|
||||
}
|
||||
|
||||
/// Creates a new [`DynamicTupleStruct`] from this tuple struct.
|
||||
fn to_dynamic_tuple_struct(&self) -> DynamicTupleStruct {
|
||||
DynamicTupleStruct {
|
||||
represented_type: self.get_represented_type_info(),
|
||||
fields: self.iter_fields().map(PartialReflect::to_dynamic).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Will return `None` if [`TypeInfo`] is not available.
|
||||
fn get_represented_tuple_struct_info(&self) -> Option<&'static TupleStructInfo> {
|
||||
@ -279,17 +290,6 @@ impl TupleStruct for DynamicTupleStruct {
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn clone_dynamic(&self) -> DynamicTupleStruct {
|
||||
DynamicTupleStruct {
|
||||
represented_type: self.represented_type,
|
||||
fields: self
|
||||
.fields
|
||||
.iter()
|
||||
.map(|value| value.clone_value())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialReflect for DynamicTupleStruct {
|
||||
@ -357,11 +357,6 @@ impl PartialReflect for DynamicTupleStruct {
|
||||
ReflectOwned::TupleStruct(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clone_value(&self) -> Box<dyn PartialReflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option<bool> {
|
||||
tuple_struct_partial_eq(self, value)
|
||||
|
@ -72,7 +72,6 @@ use thiserror::Error;
|
||||
/// # fn reflect_ref(&self) -> ReflectRef { todo!() }
|
||||
/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() }
|
||||
/// # fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
|
||||
/// # fn clone_value(&self) -> Box<dyn PartialReflect> { todo!() }
|
||||
/// # }
|
||||
/// # impl Reflect for MyStruct {
|
||||
/// # fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
|
||||
|
@ -88,7 +88,6 @@ mod sealed {
|
||||
/// # fn reflect_ref(&self) -> ReflectRef { todo!() }
|
||||
/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() }
|
||||
/// # fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
|
||||
/// # fn clone_value(&self) -> Box<dyn PartialReflect> { todo!() }
|
||||
/// # }
|
||||
/// # impl Reflect for Foo {
|
||||
/// # fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
|
||||
@ -176,7 +175,6 @@ impl<T: TypedProperty> Default for NonGenericTypeCell<T> {
|
||||
/// # fn reflect_ref(&self) -> ReflectRef { todo!() }
|
||||
/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() }
|
||||
/// # fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
|
||||
/// # fn clone_value(&self) -> Box<dyn PartialReflect> { todo!() }
|
||||
/// # }
|
||||
/// # impl<T: Reflect + Typed + TypePath> Reflect for Foo<T> {
|
||||
/// # fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
|
||||
|
@ -8,6 +8,7 @@ use bevy_ecs::{
|
||||
};
|
||||
use bevy_reflect::{PartialReflect, TypePath, TypeRegistry};
|
||||
|
||||
use crate::reflect_utils::clone_reflect_value;
|
||||
#[cfg(feature = "serialize")]
|
||||
use crate::serde::SceneSerializer;
|
||||
use bevy_ecs::component::ComponentCloneBehavior;
|
||||
@ -87,7 +88,6 @@ impl DynamicScene {
|
||||
|
||||
// Apply/ add each component to the given entity.
|
||||
for component in &scene_entity.components {
|
||||
let component = component.clone_value();
|
||||
let type_info = component.get_represented_type_info().ok_or_else(|| {
|
||||
SceneSpawnError::NoRepresentedType {
|
||||
type_path: component.reflect_type_path().to_string(),
|
||||
@ -131,7 +131,6 @@ impl DynamicScene {
|
||||
// Insert resources after all entities have been added to the world.
|
||||
// This ensures the entities are available for the resources to reference during mapping.
|
||||
for resource in &self.resources {
|
||||
let mut resource = resource.clone_value();
|
||||
let type_info = resource.get_represented_type_info().ok_or_else(|| {
|
||||
SceneSpawnError::NoRepresentedType {
|
||||
type_path: resource.reflect_type_path().to_string(),
|
||||
@ -151,6 +150,7 @@ impl DynamicScene {
|
||||
// If this component references entities in the scene, update
|
||||
// them to the entities in the world.
|
||||
if let Some(map_entities) = registration.data::<ReflectMapEntities>() {
|
||||
let mut resource = clone_reflect_value(resource.as_partial_reflect(), registration);
|
||||
SceneEntityMapper::world_scope(entity_map, world, |_, mapper| {
|
||||
map_entities.map_entities(resource.as_partial_reflect_mut(), mapper);
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
use core::any::TypeId;
|
||||
|
||||
use crate::reflect_utils::clone_reflect_value;
|
||||
use crate::{DynamicEntity, DynamicScene, SceneFilter};
|
||||
use alloc::collections::BTreeMap;
|
||||
use bevy_ecs::{
|
||||
@ -10,7 +11,7 @@ use bevy_ecs::{
|
||||
resource::Resource,
|
||||
world::World,
|
||||
};
|
||||
use bevy_reflect::{PartialReflect, ReflectFromReflect};
|
||||
use bevy_reflect::PartialReflect;
|
||||
use bevy_utils::default;
|
||||
|
||||
/// A [`DynamicScene`] builder, used to build a scene from a [`World`] by extracting some entities and resources.
|
||||
@ -303,14 +304,8 @@ impl<'w> DynamicSceneBuilder<'w> {
|
||||
.data::<ReflectComponent>()?
|
||||
.reflect(original_entity)?;
|
||||
|
||||
// Clone via `FromReflect`. Unlike `PartialReflect::clone_value` this
|
||||
// retains the original type and `ReflectSerialize` type data which is needed to
|
||||
// deserialize.
|
||||
let component = type_registration
|
||||
.data::<ReflectFromReflect>()
|
||||
.and_then(|fr| fr.from_reflect(component.as_partial_reflect()))
|
||||
.map(PartialReflect::into_partial_reflect)
|
||||
.unwrap_or_else(|| component.clone_value());
|
||||
let component =
|
||||
clone_reflect_value(component.as_partial_reflect(), type_registration);
|
||||
|
||||
entry.components.push(component);
|
||||
Some(())
|
||||
@ -383,11 +378,8 @@ impl<'w> DynamicSceneBuilder<'w> {
|
||||
.data::<ReflectResource>()?
|
||||
.reflect(self.original_world)?;
|
||||
|
||||
let resource = type_registration
|
||||
.data::<ReflectFromReflect>()
|
||||
.and_then(|fr| fr.from_reflect(resource.as_partial_reflect()))
|
||||
.map(PartialReflect::into_partial_reflect)
|
||||
.unwrap_or_else(|| resource.clone_value());
|
||||
let resource =
|
||||
clone_reflect_value(resource.as_partial_reflect(), type_registration);
|
||||
|
||||
self.extracted_resources.insert(component_id, resource);
|
||||
Some(())
|
||||
|
@ -15,6 +15,7 @@ extern crate alloc;
|
||||
mod components;
|
||||
mod dynamic_scene;
|
||||
mod dynamic_scene_builder;
|
||||
mod reflect_utils;
|
||||
mod scene;
|
||||
mod scene_filter;
|
||||
mod scene_loader;
|
||||
|
25
crates/bevy_scene/src/reflect_utils.rs
Normal file
25
crates/bevy_scene/src/reflect_utils.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use bevy_reflect::{PartialReflect, ReflectFromReflect, TypeRegistration};
|
||||
|
||||
/// Attempts to clone a [`PartialReflect`] value using various methods.
|
||||
///
|
||||
/// This first attempts to clone via [`PartialReflect::reflect_clone`].
|
||||
/// then falls back to [`ReflectFromReflect::from_reflect`],
|
||||
/// and finally [`PartialReflect::to_dynamic`] if the first two methods fail.
|
||||
///
|
||||
/// This helps ensure that the original type and type data is retained,
|
||||
/// and only returning a dynamic type if all other methods fail.
|
||||
pub(super) fn clone_reflect_value(
|
||||
value: &dyn PartialReflect,
|
||||
type_registration: &TypeRegistration,
|
||||
) -> Box<dyn PartialReflect> {
|
||||
value
|
||||
.reflect_clone()
|
||||
.map(PartialReflect::into_partial_reflect)
|
||||
.unwrap_or_else(|_| {
|
||||
type_registration
|
||||
.data::<ReflectFromReflect>()
|
||||
.and_then(|fr| fr.from_reflect(value.as_partial_reflect()))
|
||||
.map(PartialReflect::into_partial_reflect)
|
||||
.unwrap_or_else(|| value.to_dynamic())
|
||||
})
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
use core::any::TypeId;
|
||||
|
||||
use crate::reflect_utils::clone_reflect_value;
|
||||
use crate::{DynamicScene, SceneSpawnError};
|
||||
use bevy_asset::Asset;
|
||||
use bevy_ecs::{
|
||||
@ -10,7 +11,7 @@ use bevy_ecs::{
|
||||
relationship::RelationshipInsertHookMode,
|
||||
world::World,
|
||||
};
|
||||
use bevy_reflect::{PartialReflect, TypePath};
|
||||
use bevy_reflect::TypePath;
|
||||
|
||||
/// A composition of [`World`] objects.
|
||||
///
|
||||
@ -143,7 +144,9 @@ impl Scene {
|
||||
|
||||
let Some(component) = reflect_component
|
||||
.reflect(self.world.entity(scene_entity.id()))
|
||||
.map(PartialReflect::clone_value)
|
||||
.map(|component| {
|
||||
clone_reflect_value(component.as_partial_reflect(), registration)
|
||||
})
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
@ -10,7 +10,7 @@ use serde::de::DeserializeSeed;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
fn main() {
|
||||
#[derive(Reflect, Default)]
|
||||
#[derive(Reflect, Default, PartialEq, Debug)]
|
||||
#[reflect(Identifiable, Default)]
|
||||
struct Player {
|
||||
id: u32,
|
||||
@ -42,22 +42,27 @@ fn main() {
|
||||
// Because it's the same type under the hood, we can still downcast it back to the original type.
|
||||
assert!(reflected.downcast_ref::<Player>().is_some());
|
||||
|
||||
// But now let's "clone" our type using `PartialReflect::clone_value`.
|
||||
// Notice here we bind it as a `dyn PartialReflect`.
|
||||
let cloned: Box<dyn PartialReflect> = reflected.clone_value();
|
||||
// We can attempt to clone our value using `PartialReflect::reflect_clone`.
|
||||
// This will recursively call `PartialReflect::reflect_clone` on all fields of the type.
|
||||
// Or, if we had registered `ReflectClone` using `#[reflect(Clone)]`, it would simply call `Clone::clone` directly.
|
||||
let cloned: Box<dyn Reflect> = reflected.reflect_clone().unwrap();
|
||||
assert_eq!(cloned.downcast_ref::<Player>(), Some(&Player { id: 123 }));
|
||||
|
||||
// If we try and convert it to a `dyn Reflect` trait object, we'll get an error.
|
||||
assert!(cloned.try_as_reflect().is_none());
|
||||
// Another way we can "clone" our data is by converting it to a dynamic type.
|
||||
// Notice here we bind it as a `dyn PartialReflect` instead of `dyn Reflect`.
|
||||
// This is because it returns a dynamic type that simply represents the original type.
|
||||
// In this case, because `Player` is a struct, it will return a `DynamicStruct`.
|
||||
let dynamic: Box<dyn PartialReflect> = reflected.to_dynamic();
|
||||
assert!(dynamic.is_dynamic());
|
||||
|
||||
// Why is this?
|
||||
// Well the reason is that `PartialReflect::clone_value` actually creates a dynamic type.
|
||||
// Since `Player` is a struct, our trait object is actually a value of `DynamicStruct`.
|
||||
assert!(cloned.is_dynamic());
|
||||
// And if we try to convert it back to a `dyn Reflect` trait object, we'll get `None`.
|
||||
// Dynamic types cannot be directly cast to `dyn Reflect` trait objects.
|
||||
assert!(dynamic.try_as_reflect().is_none());
|
||||
|
||||
// This dynamic type is used to represent (or "proxy") the original type,
|
||||
// Generally dynamic types are used to represent (or "proxy") the original type,
|
||||
// so that we can continue to access its fields and overall structure.
|
||||
let cloned_ref = cloned.reflect_ref().as_struct().unwrap();
|
||||
let id = cloned_ref.field("id").unwrap().try_downcast_ref::<u32>();
|
||||
let dynamic_ref = dynamic.reflect_ref().as_struct().unwrap();
|
||||
let id = dynamic_ref.field("id").unwrap().try_downcast_ref::<u32>();
|
||||
assert_eq!(id, Some(&123));
|
||||
|
||||
// It also enables us to create a representation of a type without having compile-time
|
||||
@ -137,7 +142,7 @@ fn main() {
|
||||
}
|
||||
|
||||
// Lastly, while dynamic types are commonly generated via reflection methods like
|
||||
// `PartialReflect::clone_value` or via the reflection deserializers,
|
||||
// `PartialReflect::to_dynamic` or via the reflection deserializers,
|
||||
// you can also construct them manually.
|
||||
let mut my_dynamic_list = DynamicList::from_iter([1u32, 2u32, 3u32]);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user