Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/reflect/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ impl<B: Bundle + Reflect + TypePath + BundleFromComponents> FromType<B> for Refl
take: |entity| {
entity
.take::<B>()
.map(|bundle| Box::new(bundle).into_reflect())
.map(|bundle| Box::new(bundle) as Box<dyn Reflect>)
},
})
}
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/reflect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ pub fn from_reflect_with_fallback<T: Reflect + TypePath>(
);
};
assert_eq!(
value.as_any().type_id(),
value.type_id(),
id,
"The registration for the reflected `{source}` trait for the type `{name}` produced \
a value of a different type",
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_gizmos/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ impl GizmoConfigStore {
panic!("Requested config {} does not exist in `GizmoConfigStore`! Did you forget to add it using `app.init_gizmo_group<T>()`?", T::type_path());
};
// hash map invariant guarantees that &dyn Reflect is of correct type T
let ext = ext.as_any().downcast_ref().unwrap();
let ext = ext.downcast_ref().unwrap();
(config, ext)
}

Expand All @@ -134,7 +134,7 @@ impl GizmoConfigStore {
panic!("Requested config {} does not exist in `GizmoConfigStore`! Did you forget to add it using `app.init_gizmo_group<T>()`?", T::type_path());
};
// hash map invariant guarantees that &dyn Reflect is of correct type T
let ext = ext.as_any_mut().downcast_mut().unwrap();
let ext = ext.downcast_mut().unwrap();
(config, ext)
}

Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_reflect/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ mod tests {
usize::MAX
};

let b = Box::new([(); SIZE]).into_reflect();
let b: Box<dyn Reflect> = Box::new([(); SIZE]);

let array = b.reflect_ref().as_array().unwrap();

Expand Down
38 changes: 2 additions & 36 deletions crates/bevy_reflect/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,8 @@
//! ## Converting between `PartialReflect` and `Reflect`
//!
//! Since `T: Reflect` implies `T: PartialReflect`, conversion from a `dyn Reflect` to a `dyn PartialReflect`
//! trait object (upcasting) is infallible and can be performed with one of the following methods.
//! Note that these are temporary while [the language feature for dyn upcasting coercion] is experimental:
//! * [`PartialReflect::as_partial_reflect`] for `&dyn PartialReflect`
Copy link
Contributor

Choose a reason for hiding this comment

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

These removed docs are for PartialReflect::*, which are not deprecated in this PR.

//! * [`PartialReflect::as_partial_reflect_mut`] for `&mut dyn PartialReflect`
//! * [`PartialReflect::into_partial_reflect`] for `Box<dyn PartialReflect>`
//! trait object (upcasting) is infallible and is performed automatically by the compiler.
//! Passing a `&dyn Reflect` where a `&dyn PartialReflect` is expected will work seamlessly.
//!
//! For conversion in the other direction — downcasting `dyn PartialReflect` to `dyn Reflect` —
//! there are fallible methods:
Expand Down Expand Up @@ -538,7 +535,6 @@
//! [subtraits]: #the-reflection-subtraits
//! [the type registry]: #type-registration
//! [runtime cost]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch
//! [the language feature for dyn upcasting coercion]: https://github.com/rust-lang/rust/issues/65991
//! [derive macro]: derive@crate::Reflect
//! [`'static` lifetime]: https://doc.rust-lang.org/rust-by-example/scope/lifetime/static_lifetime.html#trait-bound
//! [`Function`]: crate::func::Function
Expand Down Expand Up @@ -2566,36 +2562,6 @@ mod tests {
}
}

#[test]
fn into_reflect() {
trait TestTrait: Reflect {}

#[derive(Reflect)]
struct TestStruct;

impl TestTrait for TestStruct {}

let trait_object: Box<dyn TestTrait> = Box::new(TestStruct);

// Should compile:
let _ = trait_object.into_reflect();
}

#[test]
fn as_reflect() {
trait TestTrait: Reflect {}

#[derive(Reflect)]
struct TestStruct;

impl TestTrait for TestStruct {}

let trait_object: Box<dyn TestTrait> = Box::new(TestStruct);

// Should compile:
let _ = trait_object.as_reflect();
}

#[test]
fn should_reflect_debug() {
#[derive(Reflect)]
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_reflect/src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ mod tests {
// If compiled in release mode, verify we dont overflow
usize::MAX
};
let b = Box::new(vec![(); SIZE]).into_reflect();
let b: Box<dyn Reflect> = Box::new(vec![(); SIZE]);

let list = b.reflect_ref().as_list().unwrap();

Expand Down
28 changes: 24 additions & 4 deletions crates/bevy_reflect/src/reflect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,25 +416,43 @@ pub trait Reflect: PartialReflect + DynamicTyped + Any {
/// Returns the value as a [`Box<dyn Any>`][core::any::Any].
///
/// For remote wrapper types, this will return the remote type instead.
#[deprecated(
note = "Upcasting coercion is now automatic; this method is no longer necessary. Add type annotations as needed."
)]
fn into_any(self: Box<Self>) -> Box<dyn Any>;

/// Returns the value as a [`&dyn Any`][core::any::Any].
///
/// For remote wrapper types, this will return the remote type instead.
#[deprecated(
note = "Upcasting coercion is now automatic; this method is no longer necessary. Add type annotations as needed."
)]
fn as_any(&self) -> &dyn Any;

/// Returns the value as a [`&mut dyn Any`][core::any::Any].
///
/// For remote wrapper types, this will return the remote type instead.
#[deprecated(
note = "Upcasting coercion is now automatic; this method is no longer necessary. Add type annotations as needed."
)]
fn as_any_mut(&mut self) -> &mut dyn Any;

/// Casts this type to a boxed, fully-reflected value.
#[deprecated(
note = "Upcasting coercion is now automatic; this method is no longer necessary. Add type annotations as needed."
)]
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect>;

/// Casts this type to a fully-reflected value.
#[deprecated(
note = "Upcasting coercion is now automatic; this method is no longer necessary. Add type annotations as needed."
)]
fn as_reflect(&self) -> &dyn Reflect;

/// Casts this type to a mutable, fully-reflected value.
#[deprecated(
note = "Upcasting coercion is now automatic; this method is no longer necessary. Add type annotations as needed."
)]
fn as_reflect_mut(&mut self) -> &mut dyn Reflect;

/// Performs a type-checked assignment of a reflected value to this value.
Expand Down Expand Up @@ -527,7 +545,7 @@ impl dyn Reflect {
/// For remote types, `T` should be the type itself rather than the wrapper type.
pub fn downcast<T: Any>(self: Box<dyn Reflect>) -> Result<Box<T>, Box<dyn Reflect>> {
if self.is::<T>() {
Ok(self.into_any().downcast().unwrap())
Ok(self.downcast().unwrap())
} else {
Err(self)
}
Expand Down Expand Up @@ -556,7 +574,7 @@ impl dyn Reflect {
/// [`FromReflect`]: crate::FromReflect
#[inline]
pub fn is<T: Any>(&self) -> bool {
self.as_any().type_id() == TypeId::of::<T>()
self.type_id() == TypeId::of::<T>()
}

/// Downcasts the value to type `T` by reference.
Expand All @@ -566,7 +584,8 @@ impl dyn Reflect {
/// For remote types, `T` should be the type itself rather than the wrapper type.
#[inline]
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
self.as_any().downcast_ref::<T>()
let any = self as &dyn Any;
Copy link
Member Author

Choose a reason for hiding this comment

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

Splitting this out explicitly is required to disambiguate and avoid infinite recursion.

any.downcast_ref::<T>()
}

/// Downcasts the value to type `T` by mutable reference.
Expand All @@ -576,7 +595,8 @@ impl dyn Reflect {
/// For remote types, `T` should be the type itself rather than the wrapper type.
#[inline]
pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
self.as_any_mut().downcast_mut::<T>()
let any = self as &mut dyn Any;
any.downcast_mut::<T>()
}
}

Expand Down
4 changes: 2 additions & 2 deletions examples/reflection/dynamic_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ fn main() {
let mut value: Box<dyn Reflect> = reflect_default.default();
value.apply(deserialized.as_ref());

let identifiable: &dyn Identifiable = reflect_identifiable.get(value.as_reflect()).unwrap();
let identifiable: &dyn Identifiable = reflect_identifiable.get(&*value).unwrap();
assert_eq!(identifiable.id(), 123);
}

Expand All @@ -137,7 +137,7 @@ fn main() {
let value: Box<dyn Reflect> = reflect_from_reflect
.from_reflect(deserialized.as_ref())
.unwrap();
let identifiable: &dyn Identifiable = reflect_identifiable.get(value.as_reflect()).unwrap();
let identifiable: &dyn Identifiable = reflect_identifiable.get(&*value).unwrap();
assert_eq!(identifiable.id(), 123);
}

Expand Down
4 changes: 2 additions & 2 deletions examples/reflection/type_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ fn main() {
.unwrap();

// And call our method:
reflect_damageable.damage(value.as_reflect_mut(), Box::new(25u32));
reflect_damageable.damage(&mut *value, Box::new(25u32));
assert_eq!(value.take::<Zombie>().unwrap(), Zombie { health: 75 });

// This is a simple example, but type data can be used for much more complex operations.
Expand Down Expand Up @@ -141,7 +141,7 @@ fn main() {
// Type data generated by `#[reflect_trait]` comes with a `get`, `get_mut`, and `get_boxed` method,
// which convert `&dyn Reflect` into `&dyn MyTrait`, `&mut dyn Reflect` into `&mut dyn MyTrait`,
// and `Box<dyn Reflect>` into `Box<dyn MyTrait>`, respectively.
let value: &dyn Health = reflect_health.get(value.as_reflect()).unwrap();
let value: &dyn Health = reflect_health.get(&*value).unwrap();
assert_eq!(value.health(), 50);

// Lastly, here's a list of some useful type data provided by Bevy that you might want to register for your types:
Expand Down
12 changes: 12 additions & 0 deletions release-content/migration-guides/reflection_upcasting_methods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: "Deprecated `Reflect` methods: `into_reflect`, `as_reflect`, and `as_reflect_mut`"
pull_requests: [21772]
---

The `into_any`, `as_any`, `as_any_mut`, `into_reflect`, `as_reflect`, and `as_reflect_mut` methods on the `Reflect` trait have been deprecated,
as [trait upcasting was stabilized](https://github.com/rust-lang/rust/issues/65991) in [Rust 1.86](https://doc.rust-lang.org/beta/releases.html#language-3).

In many cases, these method calls can simply be removed, and the compiler will infer what you meant to do.

In some cases however, it may need a bit of help. Type annotations (e.g. `let foo: Box<dyn Reflect>`) can be quite useful,
and if you are trying to pass in a reference to a method, judicious use of `&*` may be required to resolve compiler errors.
Loading