bevy_reflect: Add DynamicTyped
trait (#15108)
# Objective Thanks to #7207, we now have a way to validate at the type-level that a reflected value is actually the type it says it is and not just a dynamic representation of that type. `dyn PartialReflect` values _might_ be a dynamic type, but `dyn Reflect` values are guaranteed to _not_ be a dynamic type. Therefore, we can start to add methods to `Reflect` that weren't really possible before. For example, we should now be able to always get a `&'static TypeInfo`, and not just an `Option<&'static TypeInfo>`. ## Solution Add the `DynamicTyped` trait. This trait is similar to `DynamicTypePath` in that it provides a way to use the non-object-safe `Typed` trait in an object-safe way. And since all types that derive `Reflect` will also derive `Typed`, we can safely add `DynamicTyped` as a supertrait of `Reflect`. This allows us to use it when just given a `dyn Reflect` trait object. ## Testing You can test locally by running: ``` cargo test --package bevy_reflect ``` --- ## Showcase `Reflect` now has a supertrait of `DynamicTyped`, allowing `TypeInfo` to be retrieved from a `dyn Reflect` trait object without having to unwrap anything! ```rust let value: Box<dyn Reflect> = Box::new(String::from("Hello!")); // BEFORE let info: &'static TypeInfo = value.get_represented_type_info().unwrap(); // AFTER let info: &'static TypeInfo = value.reflect_type_info(); ``` ## Migration Guide `Reflect` now has a supertrait of `DynamicTyped`. If you were manually implementing `Reflect` and did not implement `Typed`, you will now need to do so.
This commit is contained in:
parent
ae80a20690
commit
37443e0f3f
@ -1632,7 +1632,7 @@ mod tests {
|
|||||||
|
|
||||||
// TypeInfo (instance)
|
// TypeInfo (instance)
|
||||||
let value: &dyn Reflect = &123_i32;
|
let value: &dyn Reflect = &123_i32;
|
||||||
let info = value.get_represented_type_info().unwrap();
|
let info = value.reflect_type_info();
|
||||||
assert!(info.is::<i32>());
|
assert!(info.is::<i32>());
|
||||||
|
|
||||||
// Struct
|
// Struct
|
||||||
@ -1653,7 +1653,7 @@ mod tests {
|
|||||||
assert_eq!(usize::type_path(), info.field_at(1).unwrap().type_path());
|
assert_eq!(usize::type_path(), info.field_at(1).unwrap().type_path());
|
||||||
|
|
||||||
let value: &dyn Reflect = &MyStruct { foo: 123, bar: 321 };
|
let value: &dyn Reflect = &MyStruct { foo: 123, bar: 321 };
|
||||||
let info = value.get_represented_type_info().unwrap();
|
let info = value.reflect_type_info();
|
||||||
assert!(info.is::<MyStruct>());
|
assert!(info.is::<MyStruct>());
|
||||||
|
|
||||||
// Struct (generic)
|
// Struct (generic)
|
||||||
@ -1675,7 +1675,7 @@ mod tests {
|
|||||||
foo: String::from("Hello!"),
|
foo: String::from("Hello!"),
|
||||||
bar: 321,
|
bar: 321,
|
||||||
};
|
};
|
||||||
let info = value.get_represented_type_info().unwrap();
|
let info = value.reflect_type_info();
|
||||||
assert!(info.is::<MyGenericStruct<String>>());
|
assert!(info.is::<MyGenericStruct<String>>());
|
||||||
|
|
||||||
// Struct (dynamic field)
|
// Struct (dynamic field)
|
||||||
@ -1705,7 +1705,7 @@ mod tests {
|
|||||||
foo: DynamicStruct::default(),
|
foo: DynamicStruct::default(),
|
||||||
bar: 321,
|
bar: 321,
|
||||||
};
|
};
|
||||||
let info = value.get_represented_type_info().unwrap();
|
let info = value.reflect_type_info();
|
||||||
assert!(info.is::<MyDynamicStruct>());
|
assert!(info.is::<MyDynamicStruct>());
|
||||||
|
|
||||||
// Tuple Struct
|
// Tuple Struct
|
||||||
@ -1731,7 +1731,7 @@ mod tests {
|
|||||||
assert!(info.field_at(1).unwrap().type_info().unwrap().is::<f32>());
|
assert!(info.field_at(1).unwrap().type_info().unwrap().is::<f32>());
|
||||||
|
|
||||||
let value: &dyn Reflect = &(123_u32, 1.23_f32, String::from("Hello!"));
|
let value: &dyn Reflect = &(123_u32, 1.23_f32, String::from("Hello!"));
|
||||||
let info = value.get_represented_type_info().unwrap();
|
let info = value.reflect_type_info();
|
||||||
assert!(info.is::<MyTuple>());
|
assert!(info.is::<MyTuple>());
|
||||||
|
|
||||||
// List
|
// List
|
||||||
@ -1746,7 +1746,7 @@ mod tests {
|
|||||||
assert_eq!(usize::type_path(), info.item_ty().path());
|
assert_eq!(usize::type_path(), info.item_ty().path());
|
||||||
|
|
||||||
let value: &dyn Reflect = &vec![123_usize];
|
let value: &dyn Reflect = &vec![123_usize];
|
||||||
let info = value.get_represented_type_info().unwrap();
|
let info = value.reflect_type_info();
|
||||||
assert!(info.is::<MyList>());
|
assert!(info.is::<MyList>());
|
||||||
|
|
||||||
// List (SmallVec)
|
// List (SmallVec)
|
||||||
@ -1763,7 +1763,7 @@ mod tests {
|
|||||||
|
|
||||||
let value: MySmallVec = smallvec::smallvec![String::default(); 2];
|
let value: MySmallVec = smallvec::smallvec![String::default(); 2];
|
||||||
let value: &dyn Reflect = &value;
|
let value: &dyn Reflect = &value;
|
||||||
let info = value.get_represented_type_info().unwrap();
|
let info = value.reflect_type_info();
|
||||||
assert!(info.is::<MySmallVec>());
|
assert!(info.is::<MySmallVec>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1779,7 +1779,7 @@ mod tests {
|
|||||||
assert_eq!(3, info.capacity());
|
assert_eq!(3, info.capacity());
|
||||||
|
|
||||||
let value: &dyn Reflect = &[1usize, 2usize, 3usize];
|
let value: &dyn Reflect = &[1usize, 2usize, 3usize];
|
||||||
let info = value.get_represented_type_info().unwrap();
|
let info = value.reflect_type_info();
|
||||||
assert!(info.is::<MyArray>());
|
assert!(info.is::<MyArray>());
|
||||||
|
|
||||||
// Cow<'static, str>
|
// Cow<'static, str>
|
||||||
@ -1791,7 +1791,7 @@ mod tests {
|
|||||||
assert_eq!(std::any::type_name::<MyCowStr>(), info.type_path());
|
assert_eq!(std::any::type_name::<MyCowStr>(), info.type_path());
|
||||||
|
|
||||||
let value: &dyn Reflect = &Cow::<'static, str>::Owned("Hello!".to_string());
|
let value: &dyn Reflect = &Cow::<'static, str>::Owned("Hello!".to_string());
|
||||||
let info = value.get_represented_type_info().unwrap();
|
let info = value.reflect_type_info();
|
||||||
assert!(info.is::<MyCowStr>());
|
assert!(info.is::<MyCowStr>());
|
||||||
|
|
||||||
// Cow<'static, [u8]>
|
// Cow<'static, [u8]>
|
||||||
@ -1806,7 +1806,7 @@ mod tests {
|
|||||||
assert_eq!(std::any::type_name::<u8>(), info.item_ty().path());
|
assert_eq!(std::any::type_name::<u8>(), info.item_ty().path());
|
||||||
|
|
||||||
let value: &dyn Reflect = &Cow::<'static, [u8]>::Owned(vec![0, 1, 2, 3]);
|
let value: &dyn Reflect = &Cow::<'static, [u8]>::Owned(vec![0, 1, 2, 3]);
|
||||||
let info = value.get_represented_type_info().unwrap();
|
let info = value.reflect_type_info();
|
||||||
assert!(info.is::<MyCowSlice>());
|
assert!(info.is::<MyCowSlice>());
|
||||||
|
|
||||||
// Map
|
// Map
|
||||||
@ -1824,7 +1824,7 @@ mod tests {
|
|||||||
assert_eq!(f32::type_path(), info.value_ty().path());
|
assert_eq!(f32::type_path(), info.value_ty().path());
|
||||||
|
|
||||||
let value: &dyn Reflect = &MyMap::new();
|
let value: &dyn Reflect = &MyMap::new();
|
||||||
let info = value.get_represented_type_info().unwrap();
|
let info = value.reflect_type_info();
|
||||||
assert!(info.is::<MyMap>());
|
assert!(info.is::<MyMap>());
|
||||||
|
|
||||||
// Value
|
// Value
|
||||||
@ -1836,7 +1836,7 @@ mod tests {
|
|||||||
assert_eq!(MyValue::type_path(), info.type_path());
|
assert_eq!(MyValue::type_path(), info.type_path());
|
||||||
|
|
||||||
let value: &dyn Reflect = &String::from("Hello!");
|
let value: &dyn Reflect = &String::from("Hello!");
|
||||||
let info = value.get_represented_type_info().unwrap();
|
let info = value.reflect_type_info();
|
||||||
assert!(info.is::<MyValue>());
|
assert!(info.is::<MyValue>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
array_debug, enum_debug, list_debug, map_debug, serde::Serializable, struct_debug, tuple_debug,
|
array_debug, enum_debug, list_debug, map_debug, serde::Serializable, struct_debug, tuple_debug,
|
||||||
tuple_struct_debug, Array, DynamicTypePath, Enum, List, Map, Set, Struct, Tuple, TupleStruct,
|
tuple_struct_debug, Array, DynamicTypePath, DynamicTyped, Enum, List, Map, Set, Struct, Tuple,
|
||||||
TypeInfo, TypePath, Typed, ValueInfo,
|
TupleStruct, TypeInfo, TypePath, Typed, ValueInfo,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
@ -408,7 +408,7 @@ where
|
|||||||
message = "`{Self}` does not implement `Reflect` so cannot be fully reflected",
|
message = "`{Self}` does not implement `Reflect` so cannot be fully reflected",
|
||||||
note = "consider annotating `{Self}` with `#[derive(Reflect)]`"
|
note = "consider annotating `{Self}` with `#[derive(Reflect)]`"
|
||||||
)]
|
)]
|
||||||
pub trait Reflect: PartialReflect + Any {
|
pub trait Reflect: PartialReflect + DynamicTyped + Any {
|
||||||
/// Returns the value as a [`Box<dyn Any>`][std::any::Any].
|
/// Returns the value as a [`Box<dyn Any>`][std::any::Any].
|
||||||
///
|
///
|
||||||
/// For remote wrapper types, this will return the remote type instead.
|
/// For remote wrapper types, this will return the remote type instead.
|
||||||
|
@ -30,12 +30,7 @@ impl<'a> Serializable<'a> {
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let info = value.get_represented_type_info().ok_or_else(|| {
|
let info = value.reflect_type_info();
|
||||||
make_custom_error(format_args!(
|
|
||||||
"type `{}` does not represent any type",
|
|
||||||
value.reflect_type_path(),
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let registration = type_registry.get(info.type_id()).ok_or_else(|| {
|
let registration = type_registry.get(info.type_id()).ok_or_else(|| {
|
||||||
make_custom_error(format_args!(
|
make_custom_error(format_args!(
|
||||||
|
@ -136,6 +136,27 @@ impl MaybeTyped for DynamicArray {}
|
|||||||
|
|
||||||
impl MaybeTyped for DynamicTuple {}
|
impl MaybeTyped for DynamicTuple {}
|
||||||
|
|
||||||
|
/// Dynamic dispatch for [`Typed`].
|
||||||
|
///
|
||||||
|
/// Since this is a supertrait of [`Reflect`] its methods can be called on a `dyn Reflect`.
|
||||||
|
///
|
||||||
|
/// [`Reflect`]: crate::Reflect
|
||||||
|
#[diagnostic::on_unimplemented(
|
||||||
|
message = "`{Self}` can not provide dynamic type information through reflection",
|
||||||
|
note = "consider annotating `{Self}` with `#[derive(Reflect)]`"
|
||||||
|
)]
|
||||||
|
pub trait DynamicTyped {
|
||||||
|
/// See [`Typed::type_info`].
|
||||||
|
fn reflect_type_info(&self) -> &'static TypeInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Typed> DynamicTyped for T {
|
||||||
|
#[inline]
|
||||||
|
fn reflect_type_info(&self) -> &'static TypeInfo {
|
||||||
|
Self::type_info()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A [`TypeInfo`]-specific error.
|
/// A [`TypeInfo`]-specific error.
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum TypeInfoError {
|
pub enum TypeInfoError {
|
||||||
|
@ -171,7 +171,7 @@ impl<T: TypedProperty> Default for NonGenericTypeCell<T> {
|
|||||||
/// # fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
|
/// # fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
|
||||||
/// # fn clone_value(&self) -> Box<dyn PartialReflect> { todo!() }
|
/// # fn clone_value(&self) -> Box<dyn PartialReflect> { todo!() }
|
||||||
/// # }
|
/// # }
|
||||||
/// # impl<T: Reflect + TypePath> Reflect for Foo<T> {
|
/// # impl<T: Reflect + Typed + TypePath> Reflect for Foo<T> {
|
||||||
/// # fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
|
/// # fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
|
||||||
/// # fn as_any(&self) -> &dyn Any { todo!() }
|
/// # fn as_any(&self) -> &dyn Any { todo!() }
|
||||||
/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() }
|
/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() }
|
||||||
|
@ -80,7 +80,7 @@ fn main() {
|
|||||||
registry.register_type_data::<Zombie, ReflectDamageable>();
|
registry.register_type_data::<Zombie, ReflectDamageable>();
|
||||||
|
|
||||||
// Then at any point we can retrieve the type data from the registry:
|
// Then at any point we can retrieve the type data from the registry:
|
||||||
let type_id = value.get_represented_type_info().unwrap().type_id();
|
let type_id = value.reflect_type_info().type_id();
|
||||||
let reflect_damageable = registry
|
let reflect_damageable = registry
|
||||||
.get_type_data::<ReflectDamageable>(type_id)
|
.get_type_data::<ReflectDamageable>(type_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -133,7 +133,7 @@ fn main() {
|
|||||||
// Now we can use `ReflectHealth` to convert `dyn Reflect` into `dyn Health`:
|
// Now we can use `ReflectHealth` to convert `dyn Reflect` into `dyn Health`:
|
||||||
let value: Box<dyn Reflect> = Box::new(Skeleton { health: 50 });
|
let value: Box<dyn Reflect> = Box::new(Skeleton { health: 50 });
|
||||||
|
|
||||||
let type_id = value.get_represented_type_info().unwrap().type_id();
|
let type_id = value.reflect_type_info().type_id();
|
||||||
let reflect_health = registry.get_type_data::<ReflectHealth>(type_id).unwrap();
|
let reflect_health = registry.get_type_data::<ReflectHealth>(type_id).unwrap();
|
||||||
|
|
||||||
// Type data generated by `#[reflect_trait]` comes with a `get`, `get_mut`, and `get_boxed` method,
|
// Type data generated by `#[reflect_trait]` comes with a `get`, `get_mut`, and `get_boxed` method,
|
||||||
|
Loading…
Reference in New Issue
Block a user