diff --git a/godot-core/src/builtin/callable.rs b/godot-core/src/builtin/callable.rs index 272c79271..e472cbdf8 100644 --- a/godot-core/src/builtin/callable.rs +++ b/godot-core/src/builtin/callable.rs @@ -446,9 +446,13 @@ impl Callable { InstanceId::try_from_i64(id) } - /// Returns the 32-bit hash value of this callable's object. - /// - /// _Godot equivalent: `hash`_ + crate::declare_hash_u32_method! { + /// Returns the 32-bit hash value of this callable's object. + /// + /// _Godot equivalent: `hash`_ + } + + #[deprecated = "renamed to hash_u32"] pub fn hash(&self) -> u32 { self.as_inner().hash().try_into().unwrap() } diff --git a/godot-core/src/builtin/collections/array.rs b/godot-core/src/builtin/collections/array.rs index cdbd5ba5d..2f3a5188b 100644 --- a/godot-core/src/builtin/collections/array.rs +++ b/godot-core/src/builtin/collections/array.rs @@ -285,14 +285,16 @@ impl Array { self.as_inner().is_empty() } - /// Returns a 32-bit integer hash value representing the array and its contents. - /// - /// Note: Arrays with equal content will always produce identical hash values. However, the - /// reverse is not true. Returning identical hash values does not imply the arrays are equal, - /// because different arrays can have identical hash values due to hash collisions. + crate::declare_hash_u32_method! { + /// Returns a 32-bit integer hash value representing the array and its contents. + /// + /// Note: Arrays with equal content will always produce identical hash values. However, the + /// reverse is not true. Returning identical hash values does not imply the arrays are equal, + /// because different arrays can have identical hash values due to hash collisions. + } + + #[deprecated = "renamed to hash_u32"] pub fn hash(&self) -> u32 { - // The GDExtension interface only deals in `i64`, but the engine's own `hash()` function - // actually returns `uint32_t`. self.as_inner().hash().try_into().unwrap() } diff --git a/godot-core/src/builtin/collections/dictionary.rs b/godot-core/src/builtin/collections/dictionary.rs index 830e096e5..ebe00e42d 100644 --- a/godot-core/src/builtin/collections/dictionary.rs +++ b/godot-core/src/builtin/collections/dictionary.rs @@ -285,7 +285,11 @@ impl Dictionary { old_value } - /// Returns a 32-bit integer hash value representing the dictionary and its contents. + crate::declare_hash_u32_method! { + /// Returns a 32-bit integer hash value representing the dictionary and its contents. + } + + #[deprecated = "renamed to hash_u32"] #[must_use] pub fn hash(&self) -> u32 { self.as_inner().hash().try_into().unwrap() diff --git a/godot-core/src/builtin/macros.rs b/godot-core/src/builtin/macros.rs index a55e7d9ff..58938c0ef 100644 --- a/godot-core/src/builtin/macros.rs +++ b/godot-core/src/builtin/macros.rs @@ -103,7 +103,9 @@ macro_rules! impl_builtin_traits_inner { ( Hash for $Type:ty ) => { impl std::hash::Hash for $Type { fn hash(&self, state: &mut H) { - self.hash().hash(state) + // The GDExtension interface only deals in `int64_t`, but the engine's own `hash()` function + // actually returns `uint32_t`. + self.hash_u32().hash(state) } } }; diff --git a/godot-core/src/builtin/mod.rs b/godot-core/src/builtin/mod.rs index be0fa446a..f8d832d72 100644 --- a/godot-core/src/builtin/mod.rs +++ b/godot-core/src/builtin/mod.rs @@ -116,6 +116,16 @@ pub mod inner { pub use crate::gen::builtin_classes::*; } +#[macro_export] +macro_rules! declare_hash_u32_method { + ($ ($docs:tt)+ ) => { + $( $docs )+ + pub fn hash_u32(&self) -> u32 { + self.as_inner().hash().try_into().expect("Godot hashes are uint32_t") + } + } +} + // ---------------------------------------------------------------------------------------------------------------------------------------------- // Conversion functions diff --git a/godot-core/src/builtin/string/gstring.rs b/godot-core/src/builtin/string/gstring.rs index 9bc168716..ca2470d67 100644 --- a/godot-core/src/builtin/string/gstring.rs +++ b/godot-core/src/builtin/string/gstring.rs @@ -156,7 +156,11 @@ impl GString { self.as_inner().length().try_into().unwrap() } - /// Returns a 32-bit integer hash value representing the string. + crate::declare_hash_u32_method! { + /// Returns a 32-bit integer hash value representing the string. + } + + #[deprecated = "renamed to hash_u32"] pub fn hash(&self) -> u32 { self.as_inner() .hash() diff --git a/godot-core/src/builtin/string/node_path.rs b/godot-core/src/builtin/string/node_path.rs index 890bdfe64..e57317235 100644 --- a/godot-core/src/builtin/string/node_path.rs +++ b/godot-core/src/builtin/string/node_path.rs @@ -117,7 +117,11 @@ impl NodePath { self.get_name_count() + self.get_subname_count() } - /// Returns a 32-bit integer hash value representing the string. + crate::declare_hash_u32_method! { + /// Returns a 32-bit integer hash value representing the string. + } + + #[deprecated = "renamed to hash_u32"] pub fn hash(&self) -> u32 { self.as_inner() .hash() diff --git a/godot-core/src/builtin/string/string_name.rs b/godot-core/src/builtin/string/string_name.rs index c15d082fe..fd9fe8644 100644 --- a/godot-core/src/builtin/string/string_name.rs +++ b/godot-core/src/builtin/string/string_name.rs @@ -139,7 +139,11 @@ impl StringName { self.as_inner().length() as usize } - /// Returns a 32-bit integer hash value representing the string. + crate::declare_hash_u32_method! { + /// Returns a 32-bit integer hash value representing the string. + } + + #[deprecated = "renamed to hash_u32"] pub fn hash(&self) -> u32 { self.as_inner() .hash() diff --git a/godot-core/src/builtin/variant/mod.rs b/godot-core/src/builtin/variant/mod.rs index 26500857b..5d189ca2e 100644 --- a/godot-core/src/builtin/variant/mod.rs +++ b/godot-core/src/builtin/variant/mod.rs @@ -310,6 +310,16 @@ impl Variant { /// Return Godot's hash value for the variant. /// /// _Godot equivalent : `@GlobalScope.hash()`_ + pub fn hash_u32(&self) -> u32 { + // @GlobalScope.hash() actually calls the VariantUtilityFunctions::hash(&Variant) function (cpp). + // This function calls the passed reference's `hash` method, which returns a uint32_t. + // Therefore, casting this function to u32 is always fine. + unsafe { interface_fn!(variant_hash)(self.var_sys()) } + .try_into() + .expect("Godot hashes are uint32_t") + } + + #[deprecated = "renamed to hash_u32 and type changed to u32"] pub fn hash(&self) -> i64 { unsafe { interface_fn!(variant_hash)(self.var_sys()) } } diff --git a/itest/rust/src/builtin_tests/containers/array_test.rs b/itest/rust/src/builtin_tests/containers/array_test.rs index ea8e3de98..5a5e4b8b4 100644 --- a/itest/rust/src/builtin_tests/containers/array_test.rs +++ b/itest/rust/src/builtin_tests/containers/array_test.rs @@ -102,7 +102,7 @@ fn array_iter_shared() { fn array_hash() { let array = array![1, 2]; // Just testing that it converts successfully from i64 to u32. - array.hash(); + array.hash_u32(); } #[itest] diff --git a/itest/rust/src/builtin_tests/containers/callable_test.rs b/itest/rust/src/builtin_tests/containers/callable_test.rs index 5d3ad1ef2..e2c055a79 100644 --- a/itest/rust/src/builtin_tests/containers/callable_test.rs +++ b/itest/rust/src/builtin_tests/containers/callable_test.rs @@ -73,14 +73,14 @@ fn callable_validity() { fn callable_hash() { let obj = CallableTestObj::new_gd(); assert_eq!( - obj.callable("assign_int").hash(), - obj.callable("assign_int").hash() + obj.callable("assign_int").hash_u32(), + obj.callable("assign_int").hash_u32() ); // Not guaranteed, but unlikely. assert_ne!( - obj.callable("assign_int").hash(), - obj.callable("stringify_int").hash() + obj.callable("assign_int").hash_u32(), + obj.callable("stringify_int").hash_u32() ); } diff --git a/itest/rust/src/builtin_tests/containers/dictionary_test.rs b/itest/rust/src/builtin_tests/containers/dictionary_test.rs index dc0cb4c8c..6bb5f838a 100644 --- a/itest/rust/src/builtin_tests/containers/dictionary_test.rs +++ b/itest/rust/src/builtin_tests/containers/dictionary_test.rs @@ -126,15 +126,22 @@ fn dictionary_hash() { "bar": true, }; - assert_eq!(a.hash(), b.hash(), "equal dictionaries have same hash"); + assert_eq!( + a.hash_u32(), + b.hash_u32(), + "equal dictionaries have same hash" + ); assert_ne!( - a.hash(), - c.hash(), + a.hash_u32(), + c.hash_u32(), "dictionaries with reordered content have different hash" ); // NaNs are not equal (since Godot 4.2) but share same hash. - assert_eq!(vdict! {772: f32::NAN}.hash(), vdict! {772: f32::NAN}.hash()); + assert_eq!( + vdict! {772: f32::NAN}.hash_u32(), + vdict! {772: f32::NAN}.hash_u32() + ); } #[itest] diff --git a/itest/rust/src/builtin_tests/containers/variant_test.rs b/itest/rust/src/builtin_tests/containers/variant_test.rs index c73b97e43..4eadfbb32 100644 --- a/itest/rust/src/builtin_tests/containers/variant_test.rs +++ b/itest/rust/src/builtin_tests/containers/variant_test.rs @@ -713,13 +713,13 @@ fn variant_hash() { ]; for variant in hash_is_not_0 { - assert_ne!(variant.hash(), 0) + assert_ne!(variant.hash_u32(), 0) } for variant in self_equal { - assert_eq!(variant.hash(), variant.hash()) + assert_eq!(variant.hash_u32(), variant.hash_u32()) } - assert_eq!(Variant::nil().hash(), 0); + assert_eq!(Variant::nil().hash_u32(), 0); // It's not guaranteed that different object will have different hash, but it is // extremely unlikely for a collision to happen.