Skip to content

Commit c169522

Browse files
bors[bot]Bromeon
andauthored
Merge #157
157: `Gd::eq()` + fix `Gd::from_variant(nil)` r=Bromeon a=Bromeon * Fix crash when converting `Variant::nil()` to `Gd<T>` * Implement `Eq` for `Gd<T>` Fixes #155. bors r+ Co-authored-by: Jan Haller <[email protected]>
2 parents 1f628e1 + ed7f30d commit c169522

File tree

2 files changed

+68
-5
lines changed

2 files changed

+68
-5
lines changed

godot-core/src/obj/gd.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ use crate::{callbacks, engine, out};
2828
/// This smart pointer can only hold _objects_ in the Godot sense: instances of Godot classes (`Node`, `RefCounted`, etc.)
2929
/// or user-declared structs (`#[derive(GodotClass)]`). It does **not** hold built-in types (`Vector3`, `Color`, `i32`).
3030
///
31+
/// `Gd<T>` never holds null objects. If you need nullability, use `Option<Gd<T>>`.
32+
///
3133
/// This smart pointer behaves differently depending on `T`'s associated types, see [`GodotClass`] for their documentation.
3234
/// In particular, the memory management strategy is fully dependent on `T`:
3335
///
@@ -591,16 +593,18 @@ impl<T: GodotClass> Share for Gd<T> {
591593

592594
impl<T: GodotClass> FromVariant for Gd<T> {
593595
fn try_from_variant(variant: &Variant) -> Result<Self, VariantConversionError> {
594-
let result = unsafe {
595-
Self::from_sys_init(|self_ptr| {
596+
let result_or_none = unsafe {
597+
Self::from_sys_init_opt(|self_ptr| {
596598
let converter = sys::builtin_fn!(object_from_variant);
597599
converter(self_ptr, variant.var_sys());
598600
})
599601
};
600602

601603
// The conversion method `variant_to_object` does NOT increment the reference-count of the object; we need to do that manually.
602604
// (This behaves differently in the opposite direction `object_to_variant`.)
603-
Ok(result.with_inc_refcount())
605+
result_or_none
606+
.map(|obj| obj.with_inc_refcount())
607+
.ok_or(VariantConversionError)
604608
}
605609
}
606610

@@ -626,6 +630,19 @@ impl<T: GodotClass> ToVariant for Gd<T> {
626630
}
627631
}
628632

633+
impl<T: GodotClass> PartialEq for Gd<T> {
634+
/// ⚠️ Returns whether two `Gd` pointers point to the same object.
635+
///
636+
/// # Panics
637+
/// When `self` or `other` is dead.
638+
fn eq(&self, other: &Self) -> bool {
639+
// Panics when one is dead
640+
self.instance_id() == other.instance_id()
641+
}
642+
}
643+
644+
impl<T: GodotClass> Eq for Gd<T> {}
645+
629646
impl<T> Display for Gd<T>
630647
where
631648
T: GodotClass<Declarer = dom::EngineDomain>,

itest/rust/src/object_test.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66

77
use crate::{expect_panic, itest};
88
use godot::bind::{godot_api, GodotClass, GodotExt};
9-
use godot::builtin::{FromVariant, GodotString, StringName, ToVariant, Variant, Vector3};
10-
use godot::engine::{file_access, Camera3D, FileAccess, Node, Node3D, Object, RefCounted};
9+
use godot::builtin::{
10+
FromVariant, GodotString, StringName, ToVariant, Variant, VariantConversionError, Vector3,
11+
};
12+
use godot::engine::{file_access, Area2D, Camera3D, FileAccess, Node, Node3D, Object, RefCounted};
1113
use godot::obj::{Base, Gd, InstanceId};
1214
use godot::obj::{Inherits, Share};
1315
use godot::sys::GodotFfi;
@@ -161,6 +163,35 @@ fn object_from_instance_id_unrelated_type() {
161163
node.free();
162164
}
163165

166+
#[itest]
167+
fn object_user_eq() {
168+
let value: i16 = 17943;
169+
let a = ObjPayload { value };
170+
let b = ObjPayload { value };
171+
172+
let a1 = Gd::new(a);
173+
let a2 = a1.share();
174+
let b1 = Gd::new(b);
175+
176+
assert_eq!(a1, a2);
177+
assert_ne!(a1, b1);
178+
assert_ne!(a2, b1);
179+
}
180+
181+
#[itest]
182+
fn object_engine_eq() {
183+
let a1 = Node3D::new_alloc();
184+
let a2 = a1.share();
185+
let b1 = Node3D::new_alloc();
186+
187+
assert_eq!(a1, a2);
188+
assert_ne!(a1, b1);
189+
assert_ne!(a2, b1);
190+
191+
a1.free();
192+
b1.free();
193+
}
194+
164195
#[itest]
165196
fn object_user_convert_variant() {
166197
let value: i16 = 17943;
@@ -225,6 +256,21 @@ fn check_convert_variant_refcount(obj: Gd<RefCounted>) {
225256
assert_eq!(obj.get_reference_count(), 1);
226257
}
227258

259+
#[itest]
260+
fn object_engine_convert_variant_nil() {
261+
let nil = Variant::nil();
262+
263+
assert_eq!(
264+
Gd::<Area2D>::try_from_variant(&nil),
265+
Err(VariantConversionError),
266+
"try_from_variant(&nil)"
267+
);
268+
269+
expect_panic("from_variant(&nil)", || {
270+
Gd::<Area2D>::from_variant(&nil);
271+
});
272+
}
273+
228274
#[itest]
229275
fn object_engine_returned_refcount() {
230276
let Some(file) = FileAccess::open("res://itest.gdextension".into(), file_access::ModeFlags::READ) else {

0 commit comments

Comments
 (0)