bevy_reflect: Re-reflect hashbrown types (#18944)

# Objective

Fixes #18943

## Solution

Reintroduces support for `hashbrown`'s `HashMap` and `HashSet` types.
These were inadvertently removed when `bevy_platform` newtyped the
`hashbrown` types.

Since we removed our `hashbrown` dependency, I gated these impls behind
a `hashbrown` feature. Not entirely sure if this is necessary since we
enabled it for `bevy_reflect` through `bevy_platform` anyways. (Complex
features still confuse me a bit so let me know if I can just remove it!)

I also went ahead and preemptively implemented `TypePath` for `PassHash`
while I was here.

## Testing

You can test that it works by adding the following to a Bevy example
based on this PR (you'll also need to include `hashbrown` of course):

```rust
#[derive(Reflect)]
struct Foo(hashbrown::HashMap<String, String>);
```

Then check it compiles with:

```
cargo check --example hello_world --no-default-features --features=bevy_reflect/hashbrown
```
This commit is contained in:
Gino Valente 2025-04-28 12:26:53 -07:00 committed by François Mockers
parent b4ee898154
commit 227e1bbf34
2 changed files with 41 additions and 0 deletions

View File

@ -33,6 +33,9 @@ debug_stack = ["std"]
## Adds reflection support to `glam` types. ## Adds reflection support to `glam` types.
glam = ["dep:glam"] glam = ["dep:glam"]
## Adds reflection support to `hashbrown` types.
hashbrown = ["dep:hashbrown"]
## Adds reflection support to `petgraph` types. ## Adds reflection support to `petgraph` types.
petgraph = ["dep:petgraph", "std"] petgraph = ["dep:petgraph", "std"]
@ -87,6 +90,7 @@ bevy_platform = { path = "../bevy_platform", version = "0.16.0", default-feature
# used by bevy-utils, but it also needs reflect impls # used by bevy-utils, but it also needs reflect impls
foldhash = { version = "0.1.3", default-features = false } foldhash = { version = "0.1.3", default-features = false }
hashbrown = { version = "0.15.1", optional = true, default-features = false }
# other # other
erased-serde = { version = "0.4", default-features = false, features = [ erased-serde = { version = "0.4", default-features = false, features = [

View File

@ -1001,6 +1001,19 @@ crate::func::macros::impl_function_traits!(::bevy_platform::collections::HashMap
> >
); );
#[cfg(feature = "hashbrown")]
impl_reflect_for_hashmap!(hashbrown::hash_map::HashMap<K, V, S>);
#[cfg(feature = "hashbrown")]
impl_type_path!(::hashbrown::hash_map::HashMap<K, V, S>);
#[cfg(all(feature = "functions", feature = "hashbrown"))]
crate::func::macros::impl_function_traits!(::hashbrown::hash_map::HashMap<K, V, S>;
<
K: FromReflect + MaybeTyped + TypePath + GetTypeRegistration + Eq + Hash,
V: FromReflect + MaybeTyped + TypePath + GetTypeRegistration,
S: TypePath + BuildHasher + Default + Send + Sync
>
);
macro_rules! impl_reflect_for_hashset { macro_rules! impl_reflect_for_hashset {
($ty:path) => { ($ty:path) => {
impl<V, S> Set for $ty impl<V, S> Set for $ty
@ -1208,6 +1221,7 @@ macro_rules! impl_reflect_for_hashset {
impl_type_path!(::bevy_platform::hash::NoOpHash); impl_type_path!(::bevy_platform::hash::NoOpHash);
impl_type_path!(::bevy_platform::hash::FixedHasher); impl_type_path!(::bevy_platform::hash::FixedHasher);
impl_type_path!(::bevy_platform::hash::PassHash);
impl_reflect_opaque!(::core::net::SocketAddr( impl_reflect_opaque!(::core::net::SocketAddr(
Clone, Clone,
Debug, Debug,
@ -1239,6 +1253,18 @@ crate::func::macros::impl_function_traits!(::bevy_platform::collections::HashSet
> >
); );
#[cfg(feature = "hashbrown")]
impl_reflect_for_hashset!(::hashbrown::hash_set::HashSet<V,S>);
#[cfg(feature = "hashbrown")]
impl_type_path!(::hashbrown::hash_set::HashSet<V, S>);
#[cfg(all(feature = "functions", feature = "hashbrown"))]
crate::func::macros::impl_function_traits!(::hashbrown::hash_set::HashSet<V, S>;
<
V: Hash + Eq + FromReflect + TypePath + GetTypeRegistration,
S: TypePath + BuildHasher + Default + Send + Sync
>
);
impl<K, V> Map for ::alloc::collections::BTreeMap<K, V> impl<K, V> Map for ::alloc::collections::BTreeMap<K, V>
where where
K: FromReflect + MaybeTyped + TypePath + GetTypeRegistration + Eq + Ord, K: FromReflect + MaybeTyped + TypePath + GetTypeRegistration + Eq + Ord,
@ -2848,4 +2874,15 @@ mod tests {
let output = <&'static str as FromReflect>::from_reflect(&expected).unwrap(); let output = <&'static str as FromReflect>::from_reflect(&expected).unwrap();
assert_eq!(expected, output); assert_eq!(expected, output);
} }
#[test]
fn should_reflect_hashmaps() {
assert_impl_all!(std::collections::HashMap<u32, f32>: Reflect);
assert_impl_all!(bevy_platform::collections::HashMap<u32, f32>: Reflect);
// We specify `foldhash::fast::RandomState` directly here since without the `default-hasher`
// feature, hashbrown uses an empty enum to force users to specify their own
#[cfg(feature = "hashbrown")]
assert_impl_all!(hashbrown::HashMap<u32, f32, foldhash::fast::RandomState>: Reflect);
}
} }