-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Merged by Bors] - Make Reflect
safe to implement
#5010
Conversation
Co-authored-by: Gingeh <[email protected]>
Hopefully fix CI?
Nice work!
I think that's fine; this naming scheme is clearer.
I wouldn't accept this justification to make it unsafe, so I won't block on the answer here.
I like the current design quite a bit; I think it's quite clear and should be easy to use. |
I agree with the name change. Although, it should be noted that it goes against the convention we've established thus far (
I think it makes sense to move |
I don't remember the conclusion: do we care about implementing |
Co-authored-by: Gino Valente <[email protected]>
Thinking about it more, I'm wondering if
|
Co-authored-by: Gino Valente <[email protected]>
Just a comment on the wording: To be consistent with the Rust API guidelines, I'd suggest calling the methods (That also allows you to deprecate the previous any method and add a blanket impl for a short transition period if that is at all needed.) |
This PR is more or less blocked on the Unique Reflect RFC; that might wildly change what is necessary here/possibly obsolete this PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like you said, I'm sure things will change a lot with the Unique Reflect RfC, but I left some comments just in case.
My main issue rn is with the inclusion of an underlying_type_id
method. It could be worth it, but I'm just having a hard time getting past its possible confusions with proxy types.
crates/bevy_reflect/src/reflect.rs
Outdated
|
||
/// Returns the value as a [`&mut dyn Any`][std::any::Any]. | ||
fn any_mut(&mut self) -> &mut dyn Any; | ||
fn as_mut_any(&mut self) -> &mut dyn Any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be as_any_mut
to match the naming of other methods in the codebase?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
quoting the rust api guidelines:
If the mut qualifier in the name of a conversion method constitutes part of the return type, it should appear as it would appear in the type. For example Vec::as_mut_slice returns a mut slice; it does what it says. This name is preferred over as_slice_mut.
But I could accept reversing it (to as_any_mut
) for consistency, as it's not in the scope of this PR to change global naming conventions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm yeah it might be good to rename this to as_any_mut
at least to match as_reflect_mut
. But we can look into renaming these according to a more Rust-approved convention later.
I'd be comfortable waiting for Unique Reflect for this, but if anyone else (@MrGVSV?) would also like to see this move forward first then I'll give this another pass and resolve my issues with it. Mainly I want to redo |
Yeah I think it makes sense to get this merged even before the RFC.
I don't think these changes should be part of this PR. They add new functionality that isn't directly in attempts to make
I really like the idea to include a |
That's fair; I suppose fixing the API is what Unique Reflect is for. I'll start working on this soon. |
crates/bevy_reflect/src/reflect.rs
Outdated
|
||
/// Returns the value as a [`&mut dyn Any`][std::any::Any]. | ||
fn any_mut(&mut self) -> &mut dyn Any; | ||
fn as_mut_any(&mut self) -> &mut dyn Any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm yeah it might be good to rename this to as_any_mut
at least to match as_reflect_mut
. But we can look into renaming these according to a more Rust-approved convention later.
I've just removed the doc links; I don't think there's a way to make them work. They won't recognize Self as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Surprisingly simple in practice; those impl files make this look way more involved than it is.
bors r+
# Objective Currently, `Reflect` is unsafe to implement because of a contract in which `any` and `any_mut` must return `self`, or `downcast` will cause UB. This PR makes `Reflect` safe, makes `downcast` not use unsafe, and eliminates this contract. ## Solution This PR adds a method to `Reflect`, `any`. It also renames the old `any` to `as_any`. `any` now takes a `Box<Self>` and returns a `Box<dyn Any>`. --- ## Changelog ### Added: - `any()` method - `represents()` method ### Changed: - `Reflect` is now a safe trait - `downcast()` is now safe - The old `any` is now called `as_any`, and `any_mut` is now `as_mut_any` ## Migration Guide - Reflect derives should not have to change anything - Manual reflect impls will need to remove the `unsafe` keyword, add `any()` implementations, and rename the old `any` and `any_mut` to `as_any` and `as_mut_any`. - Calls to `any`/`any_mut` must be changed to `as_any`/`as_mut_any` ## Points of discussion: - Should renaming `any` be avoided and instead name the new method `any_box`? - ~~Could there be a performance regression from avoiding the unsafe? I doubt it, but this change does seem to introduce redundant checks.~~ - ~~Could/should `is` and `type_id()` be implemented differently? For example, moving `is` onto `Reflect` as an `fn(&self, TypeId) -> bool`~~ Co-authored-by: PROMETHIA-27 <[email protected]>
Reflect
safe to implementReflect
safe to implement
impl dyn Reflect { | ||
/// Downcasts the value to type `T`, consuming the trait object. | ||
/// | ||
/// If the underlying value is not of type `T`, returns `Err(self)`. | ||
pub fn downcast<T: Reflect>(self: Box<dyn Reflect>) -> Result<Box<T>, Box<dyn Reflect>> { | ||
// SAFE?: Same approach used by std::any::Box::downcast. ReflectValue is always Any and type | ||
// has been checked. | ||
if self.is::<T>() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Downcast does this check internally. We should probably remove it from here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't without changing the error type from Box<dyn Reflect>
to Box<dyn Any>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spun out into #5120.
# Objective Currently, `Reflect` is unsafe to implement because of a contract in which `any` and `any_mut` must return `self`, or `downcast` will cause UB. This PR makes `Reflect` safe, makes `downcast` not use unsafe, and eliminates this contract. ## Solution This PR adds a method to `Reflect`, `any`. It also renames the old `any` to `as_any`. `any` now takes a `Box<Self>` and returns a `Box<dyn Any>`. --- ## Changelog ### Added: - `any()` method - `represents()` method ### Changed: - `Reflect` is now a safe trait - `downcast()` is now safe - The old `any` is now called `as_any`, and `any_mut` is now `as_mut_any` ## Migration Guide - Reflect derives should not have to change anything - Manual reflect impls will need to remove the `unsafe` keyword, add `any()` implementations, and rename the old `any` and `any_mut` to `as_any` and `as_mut_any`. - Calls to `any`/`any_mut` must be changed to `as_any`/`as_mut_any` ## Points of discussion: - Should renaming `any` be avoided and instead name the new method `any_box`? - ~~Could there be a performance regression from avoiding the unsafe? I doubt it, but this change does seem to introduce redundant checks.~~ - ~~Could/should `is` and `type_id()` be implemented differently? For example, moving `is` onto `Reflect` as an `fn(&self, TypeId) -> bool`~~ Co-authored-by: PROMETHIA-27 <[email protected]>
# Objective Currently, `Reflect` is unsafe to implement because of a contract in which `any` and `any_mut` must return `self`, or `downcast` will cause UB. This PR makes `Reflect` safe, makes `downcast` not use unsafe, and eliminates this contract. ## Solution This PR adds a method to `Reflect`, `any`. It also renames the old `any` to `as_any`. `any` now takes a `Box<Self>` and returns a `Box<dyn Any>`. --- ## Changelog ### Added: - `any()` method - `represents()` method ### Changed: - `Reflect` is now a safe trait - `downcast()` is now safe - The old `any` is now called `as_any`, and `any_mut` is now `as_mut_any` ## Migration Guide - Reflect derives should not have to change anything - Manual reflect impls will need to remove the `unsafe` keyword, add `any()` implementations, and rename the old `any` and `any_mut` to `as_any` and `as_mut_any`. - Calls to `any`/`any_mut` must be changed to `as_any`/`as_mut_any` ## Points of discussion: - Should renaming `any` be avoided and instead name the new method `any_box`? - ~~Could there be a performance regression from avoiding the unsafe? I doubt it, but this change does seem to introduce redundant checks.~~ - ~~Could/should `is` and `type_id()` be implemented differently? For example, moving `is` onto `Reflect` as an `fn(&self, TypeId) -> bool`~~ Co-authored-by: PROMETHIA-27 <[email protected]>
# Objective Currently, `Reflect` is unsafe to implement because of a contract in which `any` and `any_mut` must return `self`, or `downcast` will cause UB. This PR makes `Reflect` safe, makes `downcast` not use unsafe, and eliminates this contract. ## Solution This PR adds a method to `Reflect`, `any`. It also renames the old `any` to `as_any`. `any` now takes a `Box<Self>` and returns a `Box<dyn Any>`. --- ## Changelog ### Added: - `any()` method - `represents()` method ### Changed: - `Reflect` is now a safe trait - `downcast()` is now safe - The old `any` is now called `as_any`, and `any_mut` is now `as_mut_any` ## Migration Guide - Reflect derives should not have to change anything - Manual reflect impls will need to remove the `unsafe` keyword, add `any()` implementations, and rename the old `any` and `any_mut` to `as_any` and `as_mut_any`. - Calls to `any`/`any_mut` must be changed to `as_any`/`as_mut_any` ## Points of discussion: - Should renaming `any` be avoided and instead name the new method `any_box`? - ~~Could there be a performance regression from avoiding the unsafe? I doubt it, but this change does seem to introduce redundant checks.~~ - ~~Could/should `is` and `type_id()` be implemented differently? For example, moving `is` onto `Reflect` as an `fn(&self, TypeId) -> bool`~~ Co-authored-by: PROMETHIA-27 <[email protected]>
Objective
Currently,
Reflect
is unsafe to implement because of a contract in whichany
andany_mut
must returnself
, ordowncast
will cause UB. This PR makesReflect
safe, makesdowncast
not use unsafe, and eliminates this contract.Solution
This PR adds a method to
Reflect
,any
. It also renames the oldany
toas_any
.any
now takes aBox<Self>
and returns aBox<dyn Any>
.Changelog
Added:
any()
methodrepresents()
methodChanged:
Reflect
is now a safe traitdowncast()
is now safeany
is now calledas_any
, andany_mut
is nowas_mut_any
Migration Guide
unsafe
keyword, addany()
implementations, and rename the oldany
andany_mut
toas_any
andas_mut_any
.any
/any_mut
must be changed toas_any
/as_mut_any
Points of discussion:
any
be avoided and instead name the new methodany_box
?Could there be a performance regression from avoiding the unsafe? I doubt it, but this change does seem to introduce redundant checks.Could/shouldis
andtype_id()
be implemented differently? For example, movingis
ontoReflect
as anfn(&self, TypeId) -> bool