godot_core/builtin/variant/mod.rs
1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8use std::{fmt, ptr};
9
10use godot_ffi as sys;
11use sys::{ffi_methods, interface_fn, GodotFfi};
12
13use crate::builtin::{
14 GString, StringName, VariantArray, VariantDispatch, VariantOperator, VariantType,
15};
16use crate::classes;
17use crate::meta::error::{ConvertError, FromVariantError};
18use crate::meta::{
19 arg_into_ref, ffi_variant_type, ArrayElement, AsArg, ExtVariantType, FromGodot, GodotType,
20 ToGodot,
21};
22
23mod impls;
24
25/// Godot variant type, able to store a variety of different types.
26///
27/// While Godot variants do not appear very frequently in Rust due to their lack of compile-time type-safety, they are central to all sorts of
28/// dynamic APIs. For example, if you want to call a method on an object based on a string, you will need variants to store arguments and return
29/// value.
30///
31/// # Conversions
32///
33/// For type conversions, please read the [`godot::meta` module docs][crate::meta].
34///
35/// # Godot docs
36///
37/// [`Variant` (stable)](https://docs.godotengine.org/en/stable/classes/class_variant.html)
38// We rely on the layout of `Variant` being the same as Godot's layout in `borrow_slice` and `borrow_slice_mut`.
39#[repr(transparent)]
40pub struct Variant {
41 _opaque: sys::types::OpaqueVariant,
42}
43
44impl Variant {
45 /// Create an empty variant (`null` value in GDScript).
46 ///
47 /// If a Godot engine API accepts object (not variant) parameters and you'd like to pass `null`, use
48 /// [`Gd::null_arg()`][crate::obj::Gd::null_arg] instead.
49 pub fn nil() -> Self {
50 Self::default()
51 }
52
53 /// Create a variant holding a non-nil value.
54 ///
55 /// Equivalent to [`value.to_variant()`][ToGodot::to_variant], but consumes the argument.
56 pub fn from<T: ToGodot>(value: T) -> Self {
57 value.to_variant()
58 }
59
60 /// ⚠️ Convert to type `T`, panicking on failure.
61 ///
62 /// Equivalent to [`T::from_variant(&self)`][FromGodot::from_variant].
63 ///
64 /// # Panics
65 /// When this variant holds a different type.
66 pub fn to<T: FromGodot>(&self) -> T {
67 T::from_variant(self)
68 }
69
70 /// Convert to type `T`, returning `Err` on failure.
71 ///
72 /// The conversion only succeeds if the type stored in the variant matches `T`'s FFI representation.
73 /// For lenient conversions like in GDScript, use [`try_to_relaxed()`](Self::try_to_relaxed) instead.
74 ///
75 /// Equivalent to [`T::try_from_variant(&self)`][FromGodot::try_from_variant].
76 pub fn try_to<T: FromGodot>(&self) -> Result<T, ConvertError> {
77 T::try_from_variant(self)
78 }
79
80 /// Convert to `T` using Godot's less strict conversion rules.
81 ///
82 /// More lenient than [`try_to()`](Self::try_to), which only allows exact type matches.
83 /// Enables conversions between related types that Godot considers compatible under its conversion rules.
84 ///
85 /// Precisely matches GDScript's behavior to converts arguments, when a function declares a parameter of different type.
86 ///
87 /// # Conversion diagram
88 /// Exhaustive list of all possible conversions, as of Godot 4.4. The arrow `──►` means "converts to".
89 ///
90 /// ```text
91 /// * ───► Variant
92 /// * ───► itself (reflexive)
93 /// float StringName
94 /// ▲ ▲ ▲ Vector2 ◄───► Vector2i
95 /// ╱ ╲ │ Vector3 ◄───► Vector3i
96 /// ▼ ▼ ▼ Vector4 ◄───► Vector4i
97 /// bool ◄───► int GString ◄───► NodePath Rect2 ◄───► Rect2i
98 /// ╲ ╱
99 /// ╲ ╱ Array<T> ◄───► PackedArray<T>
100 /// ▼ ▼
101 /// Color Gd<T> ───► Rid
102 /// nil ───► Option<Gd<T>>
103 ///
104 /// Basis ◄───► Quaternion
105 /// ╲ ╱
106 /// ╲ ╱
107 /// ▼ ▼
108 /// Transform2D ◄───► Transform3D ◄───► Projection
109 /// ```
110 ///
111 /// # Godot implementation details
112 /// See [GDExtension interface](https://github.com/godotengine/godot/blob/4.4-stable/core/extension/gdextension_interface.h#L1353-L1364)
113 /// and [C++ implementation](https://github.com/godotengine/godot/blob/4.4-stable/core/variant/variant.cpp#L532) (Godot 4.4 at the time of
114 /// writing). The "strict" part refers to excluding certain conversions, such as between `int` and `GString`.
115 ///
116 // ASCII arsenal: / ╱ ⟋ ⧸ ⁄ ╱ ↗ ╲ \ ╲ ⟍ ⧹ ∖
117 pub fn try_to_relaxed<T: FromGodot>(&self) -> Result<T, ConvertError> {
118 try_from_variant_relaxed(self)
119 }
120
121 /// Helper function for relaxed variant conversion with panic on failure.
122 /// Similar to [`to()`](Self::to) but uses relaxed conversion rules.
123 pub(crate) fn to_relaxed_or_panic<T, F>(&self, context: F) -> T
124 where
125 T: FromGodot,
126 F: FnOnce() -> String,
127 {
128 self.try_to_relaxed::<T>()
129 .unwrap_or_else(|err| panic!("{}: {err}", context()))
130 }
131
132 /// Checks whether the variant is empty (`null` value in GDScript).
133 ///
134 /// See also [`get_type()`][Self::get_type].
135 pub fn is_nil(&self) -> bool {
136 // Use get_type() rather than sys_type(), to also cover nullptr OBJECT as NIL
137 self.get_type() == VariantType::NIL
138 }
139
140 /// Returns the type that is currently held by this variant.
141 ///
142 /// If this variant holds a type `Object` but no instance (represented as a null object pointer), then `Nil` will be returned for
143 /// consistency. This may deviate from Godot behavior -- for example, calling [`Node::get_node_or_null()`][crate::classes::Node::get_node_or_null]
144 /// with an invalid path returns a variant that has type `Object` but acts like `Nil` for all practical purposes.
145 pub fn get_type(&self) -> VariantType {
146 let sys_type = self.sys_type();
147
148 // There is a special case when the Variant has type OBJECT, but the Object* is null.
149 let is_null_object = if sys_type == sys::GDEXTENSION_VARIANT_TYPE_OBJECT {
150 // SAFETY: we checked that the raw type is OBJECT, so we can interpret the type-ptr as address of an object-ptr.
151 let object_ptr = unsafe {
152 crate::obj::raw_object_init(|type_ptr| {
153 let converter = sys::builtin_fn!(object_from_variant);
154 converter(type_ptr, sys::SysPtr::force_mut(self.var_sys()));
155 })
156 };
157
158 object_ptr.is_null()
159 } else {
160 false
161 };
162
163 if is_null_object {
164 VariantType::NIL
165 } else {
166 VariantType::from_sys(sys_type)
167 }
168 }
169
170 /// For variants holding an object, returns the object's instance ID.
171 ///
172 /// If the variant is not an object, returns `None`.
173 ///
174 /// # Panics
175 /// If the variant holds an object and that object is dead.
176 ///
177 /// If you want to detect this case, use [`try_to::<Gd<...>>()`](Self::try_to). If you want to retrieve the previous instance ID of a
178 /// freed object for whatever reason, use [`object_id_unchecked()`][Self::object_id_unchecked]. This method is only available from
179 /// Godot 4.4 onwards.
180 pub fn object_id(&self) -> Option<crate::obj::InstanceId> {
181 #[cfg(since_api = "4.4")]
182 {
183 assert!(
184 self.get_type() != VariantType::OBJECT || self.is_object_alive(),
185 "Variant::object_id(): object has been freed"
186 );
187 self.object_id_unchecked()
188 }
189
190 #[cfg(before_api = "4.4")]
191 {
192 use crate::meta::error::{ErrorKind, FromVariantError};
193 match self.try_to::<crate::obj::Gd<crate::classes::Object>>() {
194 Ok(obj) => Some(obj.instance_id_unchecked()),
195 Err(c)
196 if matches!(
197 c.kind(),
198 ErrorKind::FromVariant(FromVariantError::DeadObject)
199 ) =>
200 {
201 panic!("Variant::object_id(): object has been freed")
202 }
203 _ => None, // other conversion errors
204 }
205 }
206 }
207
208 /// For variants holding an object, returns the object's instance ID.
209 ///
210 /// If the variant is not an object, returns `None`.
211 ///
212 /// If the object is dead, the instance ID is still returned, similar to [`Gd::instance_id_unchecked()`][crate::obj::Gd::instance_id_unchecked].
213 /// Unless you have a very good reason to use this, we recommend using [`object_id()`][Self::object_id] instead.
214 #[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
215 pub fn object_id_unchecked(&self) -> Option<crate::obj::InstanceId> {
216 // SAFETY: safe to call for non-object variants (returns 0).
217 let raw_id: u64 = unsafe { interface_fn!(variant_get_object_instance_id)(self.var_sys()) };
218
219 crate::obj::InstanceId::try_from_u64(raw_id)
220 }
221
222 /// ⚠️ Calls the specified `method` with the given `args`.
223 ///
224 /// Supports `Object` as well as built-ins with methods (e.g. `Array`, `Vector3`, `GString`, etc.).
225 ///
226 /// # Panics
227 /// * If `self` is not a variant type which supports method calls.
228 /// * If the method does not exist or the signature is not compatible with the passed arguments.
229 /// * If the call causes an error.
230 #[inline]
231 pub fn call(&self, method: impl AsArg<StringName>, args: &[Variant]) -> Variant {
232 arg_into_ref!(method);
233 self.call_inner(method, args)
234 }
235
236 fn call_inner(&self, method: &StringName, args: &[Variant]) -> Variant {
237 let args_sys: Vec<_> = args.iter().map(|v| v.var_sys()).collect();
238 let mut error = sys::default_call_error();
239
240 let result = unsafe {
241 Variant::new_with_var_uninit(|variant_ptr| {
242 interface_fn!(variant_call)(
243 sys::SysPtr::force_mut(self.var_sys()),
244 method.string_sys(),
245 args_sys.as_ptr(),
246 args_sys.len() as i64,
247 variant_ptr,
248 ptr::addr_of_mut!(error),
249 )
250 })
251 };
252
253 if error.error != sys::GDEXTENSION_CALL_OK {
254 let arg_types: Vec<_> = args.iter().map(Variant::get_type).collect();
255 sys::panic_call_error(&error, "call", &arg_types);
256 }
257 result
258 }
259
260 /// Evaluates an expression using a GDScript operator.
261 ///
262 /// Returns the result of the operation, or `None` if the operation is not defined for the given operand types.
263 ///
264 /// Recommended to be used with fully-qualified call syntax.
265 /// For example, `Variant::evaluate(&a, &b, VariantOperator::Add)` is equivalent to `a + b` in GDScript.
266 pub fn evaluate(&self, rhs: &Variant, op: VariantOperator) -> Option<Variant> {
267 use crate::obj::EngineEnum;
268
269 let op_sys = op.ord() as sys::GDExtensionVariantOperator;
270 let mut is_valid = false as u8;
271
272 let result = unsafe {
273 Self::new_with_var_uninit(|variant_ptr| {
274 interface_fn!(variant_evaluate)(
275 op_sys,
276 self.var_sys(),
277 rhs.var_sys(),
278 variant_ptr,
279 ptr::addr_of_mut!(is_valid),
280 )
281 })
282 };
283
284 if is_valid == 1 {
285 Some(result)
286 } else {
287 None
288 }
289 }
290
291 pub(crate) fn sys_type(&self) -> sys::GDExtensionVariantType {
292 unsafe {
293 let ty: sys::GDExtensionVariantType = interface_fn!(variant_get_type)(self.var_sys());
294 ty
295 }
296 }
297
298 /// Return Godot's string representation of the variant.
299 ///
300 /// See also `Display` impl.
301 #[allow(unused_mut)] // result
302 pub fn stringify(&self) -> GString {
303 let mut result = GString::new();
304 unsafe {
305 interface_fn!(variant_stringify)(self.var_sys(), result.string_sys_mut());
306 }
307 result
308 }
309
310 /// Return Godot's hash value for the variant.
311 ///
312 /// _Godot equivalent : `@GlobalScope.hash()`_
313 pub fn hash_u32(&self) -> u32 {
314 // @GlobalScope.hash() actually calls the VariantUtilityFunctions::hash(&Variant) function (C++).
315 // This function calls the passed reference's `hash` method, which returns a uint32_t.
316 // Therefore, casting this function to u32 is always fine.
317 unsafe { interface_fn!(variant_hash)(self.var_sys()) }
318 .try_into()
319 .expect("Godot hashes are uint32_t")
320 }
321
322 #[deprecated = "renamed to `hash_u32` and type changed to `u32`"]
323 pub fn hash(&self) -> i64 {
324 self.hash_u32().into()
325 }
326
327 /// Interpret the `Variant` as `bool`.
328 ///
329 /// Returns `false` only if the variant's current value is the default value for its type. For example:
330 /// - `nil` for the nil type
331 /// - `false` for bool
332 /// - zero for numeric types
333 /// - empty string
334 /// - empty container (array, packed array, dictionary)
335 /// - default-constructed other builtins (e.g. zero vector, degenerate plane, zero RID, etc...)
336 pub fn booleanize(&self) -> bool {
337 // See Variant::is_zero(), roughly https://github.com/godotengine/godot/blob/master/core/variant/variant.cpp#L859.
338
339 unsafe { interface_fn!(variant_booleanize)(self.var_sys()) != 0 }
340 }
341
342 /// Assuming that this is of type `OBJECT`, checks whether the object is dead.
343 ///
344 /// Does not check again that the variant has type `OBJECT`.
345 pub(crate) fn is_object_alive(&self) -> bool {
346 sys::strict_assert_eq!(self.get_type(), VariantType::OBJECT);
347
348 crate::global::is_instance_valid(self)
349
350 // In case there are ever problems with this approach, alternative implementation:
351 // self.stringify() != "<Freed Object>".into()
352 }
353
354 // Conversions from/to Godot C++ `Variant*` pointers
355 ffi_methods! {
356 type sys::GDExtensionVariantPtr = *mut Self;
357
358 fn new_from_var_sys = new_from_sys;
359 fn new_with_var_uninit = new_with_uninit;
360 fn new_with_var_init = new_with_init;
361 fn var_sys = sys;
362 fn var_sys_mut = sys_mut;
363 }
364}
365
366// All manually implemented unsafe functions on `Variant`.
367// Deny `unsafe_op_in_unsafe_fn` so we don't forget to check safety invariants.
368#[doc(hidden)]
369#[deny(unsafe_op_in_unsafe_fn)]
370impl Variant {
371 /// Moves this variant into a variant sys pointer. This is the same as using [`GodotFfi::move_return_ptr`].
372 ///
373 /// # Safety
374 ///
375 /// `dst` must be a valid variant pointer.
376 pub(crate) unsafe fn move_into_var_ptr(self, dst: sys::GDExtensionVariantPtr) {
377 let dst: sys::GDExtensionTypePtr = dst.cast();
378 // SAFETY: `dst` is a valid Variant pointer. Additionally `Variant` doesn't behave differently for `Standard` and `Virtual`
379 // pointer calls.
380 unsafe {
381 self.move_return_ptr(dst, sys::PtrcallType::Standard);
382 }
383 }
384
385 /// Fallible construction of a `Variant` using a fallible initialization function.
386 ///
387 /// # Safety
388 ///
389 /// If `init_fn` returns `Ok(())`, then it must have initialized the pointer passed to it in accordance with [`GodotFfi::new_with_uninit`].
390 #[doc(hidden)]
391 pub unsafe fn new_with_var_uninit_result<E>(
392 init_fn: impl FnOnce(sys::GDExtensionUninitializedVariantPtr) -> Result<(), E>,
393 ) -> Result<Self, E> {
394 // Relies on current macro expansion of from_var_sys_init() having a certain implementation.
395
396 let mut raw = std::mem::MaybeUninit::<Variant>::uninit();
397
398 let var_uninit_ptr =
399 raw.as_mut_ptr() as <sys::GDExtensionVariantPtr as sys::SysPtr>::Uninit;
400
401 // SAFETY: `map` only runs the provided closure for the `Ok(())` variant, in which case `raw` has definitely been initialized.
402 init_fn(var_uninit_ptr).map(|_success| unsafe { raw.assume_init() })
403 }
404
405 /// Convert a `Variant` sys pointer to a reference to a `Variant`.
406 ///
407 /// # Safety
408 ///
409 /// `ptr` must point to a live `Variant` for the duration of `'a`.
410 pub(crate) unsafe fn borrow_var_sys<'a>(ptr: sys::GDExtensionConstVariantPtr) -> &'a Variant {
411 sys::static_assert_eq_size_align!(Variant, sys::types::OpaqueVariant);
412
413 // SAFETY: `ptr` is a pointer to a live `Variant` for the duration of `'a`.
414 unsafe { &*(ptr.cast::<Variant>()) }
415 }
416
417 /// Convert an array of `Variant` sys pointers to a slice of `Variant` references all with unbounded lifetimes.
418 ///
419 /// # Safety
420 ///
421 /// Either `variant_ptr_array` is null, or it must be safe to call [`std::slice::from_raw_parts`] with
422 /// `variant_ptr_array` cast to `*const &'a Variant` and `length`.
423 pub(crate) unsafe fn borrow_ref_slice<'a>(
424 variant_ptr_array: *const sys::GDExtensionConstVariantPtr,
425 length: usize,
426 ) -> &'a [&'a Variant] {
427 sys::static_assert_eq_size_align!(Variant, sys::types::OpaqueVariant);
428
429 // Godot may pass null to signal "no arguments" (e.g. in custom callables).
430 if variant_ptr_array.is_null() {
431 Self::strict_ensure_zero_length(length);
432 return &[];
433 }
434
435 // Note: Raw pointers and references have the same memory layout.
436 // See https://doc.rust-lang.org/reference/type-layout.html#pointers-and-references-layout.
437 let variant_ptr_array = variant_ptr_array.cast::<&Variant>();
438
439 // SAFETY: `variant_ptr_array` isn't null so it is safe to call `from_raw_parts` on the pointer cast to `*const &Variant`.
440 unsafe { std::slice::from_raw_parts(variant_ptr_array, length) }
441 }
442
443 /// Convert an array of `Variant` sys pointers to a slice with unbounded lifetime.
444 ///
445 /// # Safety
446 ///
447 /// Either `variant_array` is null, or it must be safe to call [`std::slice::from_raw_parts`] with
448 /// `variant_array` cast to `*const Variant` and `length`.
449 pub(crate) unsafe fn borrow_slice<'a>(
450 variant_array: sys::GDExtensionConstVariantPtr,
451 length: usize,
452 ) -> &'a [Variant] {
453 sys::static_assert_eq_size_align!(Variant, sys::types::OpaqueVariant);
454
455 // Godot may pass null to signal "no arguments" (e.g. in custom callables).
456 if variant_array.is_null() {
457 Self::strict_ensure_zero_length(length);
458 return &[];
459 }
460
461 let variant_array = variant_array.cast::<Variant>();
462
463 // SAFETY: `variant_array` isn't null so it is safe to call `from_raw_parts` on the pointer cast to `*const Variant`.
464 unsafe { std::slice::from_raw_parts(variant_array, length) }
465 }
466
467 /// Convert an array of `Variant` sys pointers to a mutable slice with unbounded lifetime.
468 ///
469 /// # Safety
470 ///
471 /// Either `variant_array` is null, or it must be safe to call [`std::slice::from_raw_parts_mut`] with
472 /// `variant_array` cast to `*mut Variant` and `length`.
473 pub(crate) unsafe fn borrow_slice_mut<'a>(
474 variant_array: sys::GDExtensionVariantPtr,
475 length: usize,
476 ) -> &'a mut [Variant] {
477 sys::static_assert_eq_size_align!(Variant, sys::types::OpaqueVariant);
478
479 // Godot may pass null to signal "no arguments" (e.g. in custom callables).
480 if variant_array.is_null() {
481 Self::strict_ensure_zero_length(length);
482 return &mut [];
483 }
484
485 let variant_array = variant_array.cast::<Variant>();
486
487 // SAFETY: `variant_array` isn't null so it is safe to call `from_raw_parts_mut` on the pointer cast to `*mut Variant`.
488 unsafe { std::slice::from_raw_parts_mut(variant_array, length) }
489 }
490
491 fn strict_ensure_zero_length(_length: usize) {
492 sys::strict_assert_eq!(
493 _length,
494 0,
495 "Variant::borrow_slice*(): pointer is null but length is not 0"
496 );
497 }
498
499 /// Consumes self and turns it into a sys-ptr, should be used together with [`from_owned_var_sys`](Self::from_owned_var_sys).
500 ///
501 /// This will leak memory unless `from_owned_var_sys` is called on the returned pointer.
502 pub(crate) fn into_owned_var_sys(self) -> sys::GDExtensionVariantPtr {
503 sys::static_assert_eq_size_align!(Variant, sys::types::OpaqueVariant);
504
505 let leaked = Box::into_raw(Box::new(self));
506 leaked.cast()
507 }
508
509 /// Creates a `Variant` from a sys-ptr without incrementing the refcount.
510 ///
511 /// # Safety
512 ///
513 /// * Must only be used on a pointer returned from a call to [`into_owned_var_sys`](Self::into_owned_var_sys).
514 /// * Must not be called more than once on the same pointer.
515 #[deny(unsafe_op_in_unsafe_fn)]
516 pub(crate) unsafe fn from_owned_var_sys(ptr: sys::GDExtensionVariantPtr) -> Self {
517 sys::static_assert_eq_size_align!(Variant, sys::types::OpaqueVariant);
518
519 let ptr = ptr.cast::<Self>();
520
521 // SAFETY: `ptr` was returned from a call to `into_owned_var_sys`, which means it was created by a call to
522 // `Box::into_raw`, thus we can use `Box::from_raw` here. Additionally, this is only called once on this pointer.
523 let boxed = unsafe { Box::from_raw(ptr) };
524 *boxed
525 }
526}
527
528impl ArrayElement for Variant {}
529
530// SAFETY:
531// `from_opaque` properly initializes a dereferenced pointer to an `OpaqueVariant`.
532// `std::mem::swap` is sufficient for returning a value.
533unsafe impl GodotFfi for Variant {
534 const VARIANT_TYPE: ExtVariantType = ExtVariantType::Variant;
535
536 ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
537}
538
539crate::meta::impl_godot_as_self!(Variant: ByRef);
540
541impl Default for Variant {
542 fn default() -> Self {
543 unsafe {
544 Self::new_with_var_uninit(|variant_ptr| {
545 interface_fn!(variant_new_nil)(variant_ptr);
546 })
547 }
548 }
549}
550
551impl Clone for Variant {
552 fn clone(&self) -> Self {
553 unsafe {
554 Self::new_with_var_uninit(|variant_ptr| {
555 interface_fn!(variant_new_copy)(variant_ptr, self.var_sys());
556 })
557 }
558 }
559}
560
561impl Drop for Variant {
562 fn drop(&mut self) {
563 unsafe {
564 interface_fn!(variant_destroy)(self.var_sys_mut());
565 }
566 }
567}
568
569// Variant is not Eq because it can contain floats and other types composed of floats.
570impl PartialEq for Variant {
571 fn eq(&self, other: &Self) -> bool {
572 Self::evaluate(self, other, VariantOperator::EQUAL) //.
573 .is_some_and(|v| v.to::<bool>())
574 // If there is no defined conversion (-> None), then they are non-equal.
575 }
576}
577
578impl fmt::Display for Variant {
579 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
580 let s = self.stringify();
581 write!(f, "{s}")
582 }
583}
584
585impl fmt::Debug for Variant {
586 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
587 match self.get_type() {
588 // Special case for arrays: avoids converting to VariantArray (the only Array type in VariantDispatch),
589 // which fails for typed arrays and causes a panic. This can cause an infinite loop with Debug, or abort.
590 // Can be removed if there's ever a "possibly typed" Array type (e.g. OutArray) in the library.
591 VariantType::ARRAY => {
592 // SAFETY: type is checked, and only operation is print (out data flow, no covariant in access).
593 let array = unsafe { VariantArray::from_variant_unchecked(self) };
594 array.fmt(f)
595 }
596
597 // Converting to objects before printing causes their refcount to increment, leading to an Observer effect
598 // where `Debug` actually changes the object statistics. As such, fetch information without instantiating Gd<T>.
599 VariantType::OBJECT => classes::debug_string_variant(self, f, "VariantGd"),
600
601 // VariantDispatch also includes dead objects via `FreedObject` enumerator, which maps to "<Freed Object>".
602 _ => VariantDispatch::from_variant(self).fmt(f),
603 }
604 }
605}
606
607fn try_from_variant_relaxed<T: FromGodot>(variant: &Variant) -> Result<T, ConvertError> {
608 let from_type = variant.get_type();
609 let to_type = match ffi_variant_type::<T>() {
610 ExtVariantType::Variant => {
611 // Converting to Variant always succeeds.
612 return T::try_from_variant(variant);
613 }
614 ExtVariantType::Concrete(to_type) if from_type == to_type => {
615 // If types are the same, use the regular conversion.
616 // This is both an optimization (avoids more FFI) and ensures consistency between strict and relaxed conversions for identical types.
617 return T::try_from_variant(variant);
618 }
619 ExtVariantType::Concrete(to_type) => to_type,
620 };
621
622 // Non-NIL types can technically be converted to NIL according to `variant_can_convert_strict()`, however that makes no sense -- from
623 // neither a type perspective (NIL is unit, not never type), nor a practical one. Disallow any such conversions.
624 if to_type == VariantType::NIL || !can_convert_godot_strict(from_type, to_type) {
625 return Err(FromVariantError::BadType {
626 expected: to_type,
627 actual: from_type,
628 }
629 .into_error(variant.clone()));
630 }
631
632 // Find correct from->to conversion constructor.
633 let converter = unsafe {
634 let get_constructor = interface_fn!(get_variant_to_type_constructor);
635 get_constructor(to_type.sys())
636 };
637
638 // Must be available, since we checked with `variant_can_convert_strict`.
639 let converter =
640 converter.unwrap_or_else(|| panic!("missing converter for {from_type:?} -> {to_type:?}"));
641
642 // Perform actual conversion on the FFI types. The GDExtension conversion constructor only works with types supported
643 // by Godot (i.e. GodotType), not GodotConvert (like i8).
644 let ffi_result = unsafe {
645 <<T::Via as GodotType>::Ffi as GodotFfi>::new_with_uninit(|result_ptr| {
646 converter(result_ptr, sys::SysPtr::force_mut(variant.var_sys()));
647 })
648 };
649
650 // Try to convert the FFI types back to the user type. Can still fail, e.g. i64 -> i8.
651 let via = <T::Via as GodotType>::try_from_ffi(ffi_result)?;
652 let concrete = T::try_from_godot(via)?;
653
654 Ok(concrete)
655}
656
657fn can_convert_godot_strict(from_type: VariantType, to_type: VariantType) -> bool {
658 // Godot "strict" conversion is still quite permissive.
659 // See Variant::can_convert_strict() in C++, https://github.com/godotengine/godot/blob/master/core/variant/variant.cpp#L532-L532.
660 unsafe {
661 let can_convert_fn = interface_fn!(variant_can_convert_strict);
662 can_convert_fn(from_type.sys(), to_type.sys()) == sys::conv::SYS_TRUE
663 }
664}