-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
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
adding reflection for Cow<'static, [T]> #7454
adding reflection for Cow<'static, [T]> #7454
Conversation
Welcome, new contributor! Please make sure you've read our contributing guide and we look forward to reviewing your pull request shortly ✨ |
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.
Welcome and thank you for your contribution @EliasPrescott (especially for taking the time to add tests)! Left a few comments 🙂
crates/bevy_reflect/src/impls/std.rs
Outdated
@@ -1039,6 +1039,130 @@ impl FromReflect for Cow<'static, str> { | |||
} | |||
} | |||
|
|||
impl<T: Clone + std::marker::Sync + std::marker::Send + std::hash::Hash + std::cmp::PartialEq> |
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.
Nit: I don't think we need to fully qualify these since they're already in-scope (same for the other impls). By pure convention, I think we really only qualify std::any::type_name
.
impl<T: Clone + std::marker::Sync + std::marker::Send + std::hash::Hash + std::cmp::PartialEq> | |
impl<T: Clone + Sync + Send + Hash + PartialEq> |
crates/bevy_reflect/src/impls/std.rs
Outdated
fn reflect_ref(&self) -> ReflectRef { | ||
ReflectRef::Value(self) | ||
} | ||
|
||
fn reflect_mut(&mut self) -> ReflectMut { | ||
ReflectMut::Value(self) | ||
} | ||
|
||
fn reflect_owned(self: Box<Self>) -> ReflectOwned { | ||
ReflectOwned::Value(self) | ||
} |
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.
I think we should consider making this implement Array
and return Reflect***::Array
instead. A slice acts more like an array than a pure value (you could argue that str
should also work like this, but I think that's a special enough case).
This also means we should be able to replace the Clone
, Hash
, and PartialEq
bounds on T
with a simple Reflect
bound (and FromReflect
for the FromReflect
impl). I'm not 100% sure if doing that will work as easily as I'm making it sound haha, but I believe that going the Array
route is probably the better option.
crates/bevy_reflect/src/impls/std.rs
Outdated
@@ -1039,6 +1039,131 @@ impl FromReflect for Cow<'static, str> { | |||
} | |||
} | |||
|
|||
impl<T: FromReflect + Clone + Send + Sync> Array for Cow<'static, [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.
I don't think the Clone
bound is necessary (for any of the impls). I'd recommend taking a look at the trait implementations for [T; N]
.
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.
I thought Clone would be necessary, because otherwise I get the error: [T]: ToOwned is not satisfied in Cow<'static, [T]>.
Here are the docs on Cow where it mentions the ToOwned bound. I would like to remove the Clone bounds if possible, but that's the only way I've been able to sneak past the type checker so far.
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.
Are you sure that the Send
and Sync
bounds are necessary?
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.
Are you sure that the
Send
andSync
bounds are necessary?
Good point, they should be added by the FromReflect
bound already.
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.
Thanks for pointing that out. I removed them in the latest commit. I think I added them before the FromReflect
bound was there and I didn't realize they were no longer needed.
crates/bevy_reflect/src/impls/std.rs
Outdated
impl<T: FromReflect + Clone + Send + Sync> Typed for Cow<'static, [T]> { | ||
fn type_info() -> &'static TypeInfo { | ||
static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new(); | ||
CELL.get_or_set(|| TypeInfo::Array(ArrayInfo::new::<Self, T>(0))) |
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, it's kinda weird that we'd set the capacity to 0
here. I wonder if there's a better way of doing this? 🤔
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.
Yeah I was going to ask about that. I thought a slice type didn't have a capacity, unless we consider the hardware's max isize as a capacity.
Trying to implement this almost made me feel like it'd be more natural to be returning a Reflect***::List instead of the Array case. I haven't tried that yet, but that could keep us from deciding on a random capacity.
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.
Can you insert/remove data on a slice? If so then yeah List
would be better. Otherwise, I’m not sure of the best approach...
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.
You can't insert/remove data on a regular slice, but you can on a Cow slice by doing a to_mut
and then inserting/removing.
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.
Basically a Cow<'static, [T]>
is a Vec<T>
that can optionally store ALL of its data in static memory. Therefore it's closer to being a Vec<T>
than [T; N]
, so it should be treated as a List
in reflection.
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.
Co-authored-by: MinerSebas <66798382+MinerSebas@users.noreply.github.com>
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 fine overall, just one major issue and a few nits.
crates/bevy_reflect/src/impls/std.rs
Outdated
} | ||
|
||
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> { | ||
self.iter().map(Reflect::clone_value).collect() |
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.
You can save it from doing an unnecessary clone if you have a Cow::Owned
by switching this to self.into_owned().into_iter()
. That would only clone the data if it's currently borrowed, otherwise it would reuse the owned data. Although that currently triggers a false-positive in clippy, so it will need an #[allow]
tag added.
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.
Thanks for pointing that out! Also, thank you for the allow tag idea. I was having lots of trouble getting around that Clippy lint.
crates/bevy_reflect/src/impls/std.rs
Outdated
@@ -1039,6 +1039,131 @@ impl FromReflect for Cow<'static, str> { | |||
} | |||
} | |||
|
|||
impl<T: FromReflect + Clone + Send + Sync> Array for Cow<'static, [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.
Are you sure that the Send
and Sync
bounds are necessary?
crates/bevy_reflect/src/impls/std.rs
Outdated
impl<T: FromReflect + Clone + Send + Sync> Typed for Cow<'static, [T]> { | ||
fn type_info() -> &'static TypeInfo { | ||
static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new(); | ||
CELL.get_or_set(|| TypeInfo::Array(ArrayInfo::new::<Self, T>(0))) |
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.
Basically a Cow<'static, [T]>
is a Vec<T>
that can optionally store ALL of its data in static memory. Therefore it's closer to being a Vec<T>
than [T; N]
, so it should be treated as a List
in reflection.
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.
One more.
crates/bevy_reflect/src/impls/std.rs
Outdated
} | ||
} | ||
|
||
impl<T: FromReflect + Clone + Send + Sync> GetTypeRegistration for Cow<'static, [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.
ReflectDeserialize
and ReflectSerialize
require that the type implements Deserialize
and Serialize
, respectively.
impl<T: FromReflect + Clone + Send + Sync> GetTypeRegistration for Cow<'static, [T]> { | |
impl<'de, T: FromReflect + Clone + Serialize + Deserialize<'de>> GetTypeRegistration for Cow<'static, [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.
However... what if we want to be able to reflect one that isn't serializable? Now that's not possible.
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.
I added the Deserialize
and Serialize
trait bounds. Thankfully, I had just read this post that talks about higher-ranked trait bounds, because that came in very handy 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.
I disagree that we should add Serialize
and Deserialize
bounds. Again, I recommend looking at the implementation for [T; N]
rather than for Cow<'static, str>
. The reason we don't have those trait bounds there is because it really limits what T
can be.
ReflectSerialize
and ReflectDeserialize
can always be registered manually (like we do with many other types).
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.
That makes sense. I changed the GetTypeRegistration
impl to match the other generic types in std.rs.
crates/bevy_reflect/src/impls/std.rs
Outdated
|
||
impl<T: FromReflect + Clone> Typed for Cow<'static, [T]> { | ||
fn type_info() -> &'static TypeInfo { | ||
static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new(); |
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.
I think this should use the GenericTypeInfoCell
, right?
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.
Yes, I believe so. Thanks for pointing that out!
Example |
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.
LGTM! Thanks for working on this! 😄
bors try |
Thank you so much for helping me! Also, thank you to @ItsDoot and @MinerSebas for reviewing and suggesting changes. You were all very helpful and I learned a lot! |
@EliasPrescott Sorry for the delay with this one. Could you rebase this PR on current main? I think it might be broken after #7467 |
No worries! I synced my fork with main and updated the |
Fixed the merge conflicts for you. Once I have another approval that I didn't screw that up I'll merge this. |
@alice-i-cecile there still seems to be some CI errors |
Ah, formatting issues. @EliasPrescott want to format and push for us? |
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 otherwise, from what I can recall.
impl<T: PathOnly> PathOnly for [T] where [T]: ToOwned {} | ||
|
||
impl<T: TypePath> TypePath for [T] | ||
where | ||
[T]: ToOwned, | ||
{ | ||
fn type_path() -> &'static str { | ||
static CELL: GenericTypePathCell = GenericTypePathCell::new(); | ||
CELL.get_or_insert::<Self, _>(|| format!("[{}]", <T>::type_path())) | ||
} | ||
|
||
fn short_type_path() -> &'static str { | ||
static CELL: GenericTypePathCell = GenericTypePathCell::new(); | ||
CELL.get_or_insert::<Self, _>(|| format!("[{}]", <T>::short_type_path())) | ||
} | ||
} | ||
|
||
impl<T: ToOwned> PathOnly for 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.
This is new code that I had to add after merging the latest changes into my fork. I want feedback on it especially because I'm not sure if adding blanket impls like this is a good practice or not.
Also, I wanted to ask if I need to expand the type returned from type_path()
for [T]
. As far as I know, the slice type doesn't actually live in any namespace, but if it did, we could return something like core::slice::<[T]>
instead.
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.
This looks good to me. The blanket impl should be fine as far as I can tell. I'll be honest, I'm not sure why we have this trait over just using ToOwned
directly, but either way this should be fine.
impl<T: PathOnly> PathOnly for [T] where [T]: ToOwned {} | ||
|
||
impl<T: TypePath> TypePath for [T] | ||
where | ||
[T]: ToOwned, | ||
{ | ||
fn type_path() -> &'static str { | ||
static CELL: GenericTypePathCell = GenericTypePathCell::new(); | ||
CELL.get_or_insert::<Self, _>(|| format!("[{}]", <T>::type_path())) | ||
} | ||
|
||
fn short_type_path() -> &'static str { | ||
static CELL: GenericTypePathCell = GenericTypePathCell::new(); | ||
CELL.get_or_insert::<Self, _>(|| format!("[{}]", <T>::short_type_path())) | ||
} | ||
} | ||
|
||
impl<T: ToOwned> PathOnly for 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.
This looks good to me. The blanket impl should be fine as far as I can tell. I'll be honest, I'm not sure why we have this trait over just using ToOwned
directly, but either way this should be fine.
# Objective - Implementing reflection for Cow<'static, [T]> - Hopefully fixes bevyengine#7429 ## Solution - Implementing Reflect, Typed, GetTypeRegistration, and FromReflect for Cow<'static, [T]> --- ## Notes I have not used bevy_reflection much yet, so I may not fully understand all the use cases. This is also my first attempt at contributing, so I would appreciate any feedback or recommendations for changes. I tried to add cases for using Cow<'static, str> and Cow<'static, [u8]> to some of the bevy_reflect tests, but I can't guarantee those tests are comprehensive enough. --------- Co-authored-by: MinerSebas <66798382+MinerSebas@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Objective
Solution
Notes
I have not used bevy_reflection much yet, so I may not fully understand all the use cases. This is also my first attempt at contributing, so I would appreciate any feedback or recommendations for changes. I tried to add cases for using Cow<'static, str> and Cow<'static, [u8]> to some of the bevy_reflect tests, but I can't guarantee those tests are comprehensive enough.