-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Implement Reflect for Box<dyn Reflect> #3400
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
Implement Reflect for Box<dyn Reflect> #3400
Conversation
Awesome, thank you. This is a nice addition, and your test looks nice :) |
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.
Looks good to me!
A possible improvement would be to show its usage in the reflection.rs
example, perhaps by adding a field of type Box<dyn Reflect>
in Foo
?
Yup "boxing boxes" is definitely a concern here, although the scope of the problem is different because in core bevy apis we aren't implicitly boxing However Thaaaat being said, having reflection work for "everything", including Boxed reflect types, does seem like it could be valuable. Can we outline specific "real world" scenarios that need this (that can't be resolved in other ways?). So far I've only seen that a user wants to do this, which isn't enough context to evaluate the need here. |
Note that any
Given all of that I think it's reasonable to still add the impl, and thoroughly document places where nested boxing might occur by mistake. And consider adding that runtime check as a guard. |
Here's the runtime workaround : https://discord.com/channels/442252698964721669/443150878111694848/981137955773095936 (I didn't try it yet)
fn try_downcast<T, K>(k: K) -> Result<T, K>
where
T: 'static,
K: 'static,
{
let mut k = Some(k);
if let Some(k) = <dyn std::any::Any>::downcast_mut::<Option<T>>(&mut k) {
Ok(k.take().unwrap())
} else {
Err(k.unwrap())
}
}
fn box_trait<T: 'static + Trait>(val: T) -> Box<dyn Trait> {
try_downcast::<Box<dyn Trait>, _>(val)
.unwrap_or_else(|val| Box::new(val))
} |
as a real-world example; working on my editor I've stumbled across this. I want to send messages over network between the editor and the game client, and those messages are serialized into yaml (could be anything, picked arbitrarily) via reflection. In order to send reflected types, I sometimes need to send a Box (serialized components), but it's not Reflect so I ended up implementing a ReflectObject newtype around it. This PR would allow me to throw that (likely somewhat buggy) code out and reduce annoying boilerplate/restrictions |
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.
Looks decent, but I have a few questions:
crates/bevy_reflect/src/reflect.rs
Outdated
|
||
unsafe impl Reflect for Box<dyn Reflect> { | ||
fn type_name(&self) -> &str { | ||
self.deref().type_name() |
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.
why is .deref()
necessary here? Clarity?
@franciscoaguirre Would you be able to rebase this off |
Adding the @franciscoaguirre let us know if you'd rather continue working on this yourself and I'll remove it! :) |
@MrGVSV Yeah I can work on this. There was so much debate that I got lost 😂 and I wasn't having much time. I have more time now so I can get it done |
@franciscoaguirre Are you still intending to work on this? |
@rdrpenguin04 I've been heavily caught in finishing university. Now that I'll soon graduate I'll have time for open source and I'd love to contribute to Bevy. That said, I can only work on it in January, since the holidays are coming up. I've seen people requesting this PR to be merged so if you need it before holidays I don't mind you taking the torch. If not, I can work on it in January :) |
94dab85
to
eb12d3c
Compare
@rdrpenguin04 I got around to rebasing and updating the code with the latest on main 😄 |
@franciscoaguirre FYI you have at least one test panicking, looks like it's directly related to your change. |
Yep, fixed it. The thing is, I was implementing |
I have no idea, I don't have enough domain knowledge to answer whether we should implement the dynamic version or not. My gut feeling is that |
Maintainers: What are we doing with this PR? It's been S-Ready-For-Final-Review for a while now |
Deferring to the Reflection SMEs. I'll bring it up on Discord. |
Sorry to everyone that's been waiting on this :/ I'm currently looking into a possible alternative approach that aims to avoid the double-boxing issue by going through remote reflection. However, if that doesn't work out or it runs into the same problems, then we'll probably move forward with this PR. I'll keep experimenting and return with details soon! Either way, I'd like to get something in for |
I've just pushed #14776 if anyone wants to give it a look! The TL;DR is that we can avoid the risk of double-boxing by adding a separate use bevy_reflect::boxed::ReflectBox;
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct Player {
#[reflect(remote = ReflectBox<dyn Reflect>)]
weapon: Box<dyn Reflect>,
} The main consideration is that this So the question becomes, do we go for the more flexible PR (this one) or the more conservative PR (#14776)? I'm leaning towards starting out conservative to see how far that can take us. We can then revisit this PR in the future if necessary. However, I'm more than happy to hear alternative perspectives or even other ideas! Also, I wanted to point out that another cool benefit to If we do end up going with this PR, it might be worthwhile trying to get that behavior to work as well (possibly using a similar approach as |
Closing in favor of #14776. |
Objective
Closes #3392
Solution
Used code snippet from @Davier and made a few changes on
DynamicList
for the type checker to work.