Skip to content

Commit 8a62088

Browse files
authored
Merge pull request #914 from godot-rust/feature/variant-object-id
Add `Variant::object_id()`
2 parents f8a0939 + 9a0d8ba commit 8a62088

File tree

6 files changed

+67
-15
lines changed

6 files changed

+67
-15
lines changed

godot-core/src/builtin/callable.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,8 @@ impl Callable {
219219

220220
/// Returns the object on which this callable is called.
221221
///
222-
/// Returns `None` when this callable doesn't have any target object to call a method on, regardless of
223-
/// if the method exists for that target or not.
222+
/// Returns `None` when this callable doesn't have any target object to call a method on (regardless of whether the method exists for that
223+
/// target or not). Also returns `None` if the object is dead. You can differentiate these two cases using [`object_id()`][Self::object_id].
224224
///
225225
/// _Godot equivalent: `get_object`_
226226
pub fn object(&self) -> Option<Gd<Object>> {
@@ -236,6 +236,8 @@ impl Callable {
236236
///
237237
/// Returns `None` when this callable doesn't have any target to call a method on.
238238
///
239+
/// If the pointed-to object is dead, the ID will still be returned. Use [`object()`][Self::object] to check for liveness.
240+
///
239241
/// _Godot equivalent: `get_object_id`_
240242
pub fn object_id(&self) -> Option<InstanceId> {
241243
let id = self.as_inner().get_object_id();

godot-core/src/builtin/signal.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ impl Signal {
123123

124124
/// Returns the object to which this signal belongs.
125125
///
126-
/// Returns [`None`] when this signal doesn't have any object.
126+
/// Returns [`None`] when this signal doesn't have any object, or the object is dead. You can differentiate these two situations using
127+
/// [`object_id()`][Self::object_id].
127128
///
128129
/// _Godot equivalent: `get_object`_
129130
pub fn object(&self) -> Option<Gd<Object>> {
@@ -137,6 +138,8 @@ impl Signal {
137138
///
138139
/// Returns [`None`] when this signal doesn't have any object.
139140
///
141+
/// If the pointed-to object is dead, the ID will still be returned. Use [`object()`][Self::object] to check for liveness.
142+
///
140143
/// _Godot equivalent: `get_object_id`_
141144
pub fn object_id(&self) -> Option<InstanceId> {
142145
let id = self.as_inner().get_object_id();

godot-core/src/builtin/variant/mod.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@ impl Variant {
4747

4848
/// Create a variant holding a non-nil value.
4949
///
50-
/// Equivalent to `value.to_variant()`.
50+
/// Equivalent to [`value.to_variant()`][ToGodot::to_variant], but consumes the argument.
5151
pub fn from<T: ToGodot>(value: T) -> Self {
5252
value.to_variant()
5353
}
5454

5555
/// ⚠️ Convert to type `T`, panicking on failure.
5656
///
57-
/// Equivalent to `T::from_variant(&self)`.
57+
/// Equivalent to [`T::from_variant(&self)`][FromGodot::from_variant].
5858
///
5959
/// # Panics
6060
/// When this variant holds a different type.
@@ -64,14 +64,14 @@ impl Variant {
6464

6565
/// Convert to type `T`, returning `Err` on failure.
6666
///
67-
/// Equivalent to `T::try_from_variant(&self)`.
67+
/// Equivalent to [`T::try_from_variant(&self)`][FromGodot::try_from_variant].
6868
pub fn try_to<T: FromGodot>(&self) -> Result<T, ConvertError> {
6969
T::try_from_variant(self)
7070
}
7171

7272
/// Checks whether the variant is empty (`null` value in GDScript).
7373
///
74-
/// See also [`Self::get_type`].
74+
/// See also [`get_type()`][Self::get_type].
7575
pub fn is_nil(&self) -> bool {
7676
// Use get_type() rather than sys_type(), to also cover nullptr OBJECT as NIL
7777
self.get_type() == VariantType::NIL
@@ -80,8 +80,8 @@ impl Variant {
8080
/// Returns the type that is currently held by this variant.
8181
///
8282
/// If this variant holds a type `Object` but no instance (represented as a null object pointer), then `Nil` will be returned for
83-
/// consistency. This may deviate from Godot behavior -- for example, calling `Node::get_node_or_null()` with an invalid
84-
/// path returns a variant that has type `Object` but acts like `Nil` for all practical purposes.
83+
/// consistency. This may deviate from Godot behavior -- for example, calling [`Node::get_node_or_null()`][crate::classes::Node::get_node_or_null]
84+
/// with an invalid path returns a variant that has type `Object` but acts like `Nil` for all practical purposes.
8585
pub fn get_type(&self) -> VariantType {
8686
let sys_type = self.sys_type();
8787

@@ -107,6 +107,20 @@ impl Variant {
107107
}
108108
}
109109

110+
/// For variants holding an object, returns the object's instance ID.
111+
///
112+
/// If the variant is not an object, returns `None`.
113+
///
114+
/// If the object is dead, the instance ID is still returned. Use [`Variant::try_to::<Gd<T>>()`][Self::try_to]
115+
/// to retrieve only live objects.
116+
#[cfg(since_api = "4.4")]
117+
pub fn object_id(&self) -> Option<crate::obj::InstanceId> {
118+
// SAFETY: safe to call for non-object variants (returns 0).
119+
let raw_id: u64 = unsafe { interface_fn!(variant_get_object_instance_id)(self.var_sys()) };
120+
121+
crate::obj::InstanceId::try_from_u64(raw_id)
122+
}
123+
110124
/// ⚠️ Calls the specified `method` with the given `args`.
111125
///
112126
/// Supports `Object` as well as built-ins with methods (e.g. `Array`, `Vector3`, `GString`, etc.).

itest/rust/src/builtin_tests/containers/callable_test.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,19 @@ fn callable_hash() {
6767

6868
#[itest]
6969
fn callable_object_method() {
70-
let obj = CallableTestObj::new_gd();
71-
let callable = obj.callable("foo");
70+
let object = CallableTestObj::new_gd();
71+
let object_id = object.instance_id();
72+
let callable = object.callable("foo");
7273

73-
assert_eq!(callable.object(), Some(obj.clone().upcast::<Object>()));
74-
assert_eq!(callable.object_id(), Some(obj.instance_id()));
74+
assert_eq!(callable.object(), Some(object.clone().upcast::<Object>()));
75+
assert_eq!(callable.object_id(), Some(object_id));
7576
assert_eq!(callable.method_name(), Some("foo".into()));
7677

78+
// Invalidating the object still returns the old ID, however not the object.
79+
drop(object);
80+
assert_eq!(callable.object_id(), Some(object_id));
81+
assert_eq!(callable.object(), None);
82+
7783
assert_eq!(Callable::invalid().object(), None);
7884
assert_eq!(Callable::invalid().object_id(), None);
7985
assert_eq!(Callable::invalid().method_name(), None);

itest/rust/src/builtin_tests/containers/signal_test.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,21 @@ fn signals() {
9292
#[itest]
9393
fn instantiate_signal() {
9494
let mut object = RefCounted::new_gd();
95+
let object_id = object.instance_id();
9596

9697
object.add_user_signal("test_signal");
9798

9899
let signal = Signal::from_object_signal(&object, "test_signal");
99100

100101
assert!(!signal.is_null());
101102
assert_eq!(signal.name(), StringName::from("test_signal"));
102-
assert_eq!(signal.object().unwrap(), object.clone().upcast());
103-
assert_eq!(signal.object_id().unwrap(), object.instance_id());
103+
assert_eq!(signal.object(), Some(object.clone().upcast()));
104+
assert_eq!(signal.object_id(), Some(object_id));
105+
106+
// Invalidating the object still returns the old ID, however not the object.
107+
drop(object);
108+
assert_eq!(signal.object_id(), Some(object_id));
109+
assert_eq!(signal.object(), None);
104110
}
105111

106112
#[itest]

itest/rust/src/builtin_tests/containers/variant_test.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,27 @@ fn variant_get_type() {
186186
assert_eq!(variant.get_type(), VariantType::BASIS)
187187
}
188188

189+
#[cfg(since_api = "4.4")]
190+
#[itest]
191+
fn variant_object_id() {
192+
let variant = Variant::nil();
193+
assert_eq!(variant.object_id(), None);
194+
195+
let variant = Variant::from(77);
196+
assert_eq!(variant.object_id(), None);
197+
198+
let node = Node::new_alloc();
199+
let id = node.instance_id();
200+
201+
let variant = node.to_variant();
202+
assert_eq!(variant.object_id(), Some(id));
203+
204+
node.free();
205+
206+
// When freed, variant still returns the object ID.
207+
assert_eq!(variant.object_id(), Some(id));
208+
}
209+
189210
#[itest]
190211
fn variant_equal() {
191212
assert_eq!(Variant::nil(), ().to_variant());

0 commit comments

Comments
 (0)