Skip to content

Commit

Permalink
Use Gd::null_arg(), consistent with Variant::nil()
Browse files Browse the repository at this point in the history
  • Loading branch information
Bromeon committed Jul 20, 2024
1 parent df48750 commit 84505ba
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 55 deletions.
3 changes: 3 additions & 0 deletions godot-core/src/builtin/variant/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ pub struct Variant {

impl Variant {
/// Create an empty variant (`null` value in GDScript).
///
/// If a Godot engine API accepts object (not variant) parameters and you'd like to pass `null`, use
/// [`Gd::null_arg()`][crate::obj::Gd::null_arg] instead.
pub fn nil() -> Self {
Self::default()
}
Expand Down
70 changes: 49 additions & 21 deletions godot-core/src/obj/gd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ use crate::{classes, out};
/// This smart pointer can only hold _objects_ in the Godot sense: instances of Godot classes (`Node`, `RefCounted`, etc.)
/// or user-declared structs (declared with `#[derive(GodotClass)]`). It does **not** hold built-in types (`Vector3`, `Color`, `i32`).
///
/// `Gd<T>` never holds null objects. If you need nullability, use `Option<Gd<T>>`. To pass null objects to engine APIs, use
/// [`NullArg`][crate::obj::NullArg].
/// `Gd<T>` never holds null objects. If you need nullability, use `Option<Gd<T>>`. To pass null objects to engine APIs, you can
/// additionally use [`Gd::null_arg()`] as a shorthand.
///
/// # Memory management
///
Expand Down Expand Up @@ -466,26 +466,7 @@ impl<T: GodotClass> Gd<T> {
{
self.raw.script_sys()
}
}

impl<T: GodotClass> Deref for Gd<T> {
// Target is always an engine class:
// * if T is an engine class => T
// * if T is a user class => T::Base
type Target = GdDerefTarget<T>;

fn deref(&self) -> &Self::Target {
self.raw.as_target()
}
}

impl<T: GodotClass> DerefMut for Gd<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.raw.as_target_mut()
}
}

impl<T: GodotClass> Gd<T> {
/// Runs `init_fn` on the address of a pointer (initialized to null). If that pointer is still null after the `init_fn` call,
/// then `None` will be returned; otherwise `Gd::from_obj_sys(ptr)`.
///
Expand Down Expand Up @@ -635,9 +616,56 @@ where
}
}

impl<T> Gd<T>
where
T: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
{
/// Represents `null` when passing an object argument to Godot.
///
/// This expression is only intended for function argument lists. It can be used whenever a Godot signature accepts
/// [`AsObjectArg<T>`][crate::obj::AsObjectArg]. `Gd::null_arg()` as an argument is equivalent to `Option::<Gd<T>>::None`, but less wordy.
///
/// To work with objects that can be null, use `Option<Gd<T>>` instead. For APIs that accept `Variant`, you can pass [`Variant::nil()`].
///
/// # Nullability
/// <div class="warning">
/// The GDExtension API does not inform about nullability of its function parameters. It is up to you to verify that the arguments you pass
/// are only null when this is allowed. Doing this wrong should be safe, but can lead to the function call failing.
/// </div>
///
/// # Example
/// ```no_run
/// # fn some_shape() -> Gd<GltfPhysicsShape> { unimplemented!() }
/// use godot::prelude::*;
/// use godot::classes::GltfPhysicsShape;
///
/// let mut shape: Gd<GltfPhysicsShape> = some_shape();
/// shape.set_importer_mesh(Gd::null_arg());
pub fn null_arg() -> crate::obj::ObjectNullArg<T> {
crate::obj::ObjectNullArg(std::marker::PhantomData)
}
}

// ----------------------------------------------------------------------------------------------------------------------------------------------
// Trait impls

impl<T: GodotClass> Deref for Gd<T> {
// Target is always an engine class:
// * if T is an engine class => T
// * if T is a user class => T::Base
type Target = GdDerefTarget<T>;

fn deref(&self) -> &Self::Target {
self.raw.as_target()
}
}

impl<T: GodotClass> DerefMut for Gd<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.raw.as_target_mut()
}
}

impl<T: GodotClass> GodotConvert for Gd<T> {
type Via = Gd<T>;
}
Expand Down
4 changes: 2 additions & 2 deletions godot-core/src/obj/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@
//! * [`GodotClass`], which is implemented for every class that Godot can work with (either engine- or user-provided).
//! * [`Gd`], a smart pointer that manages instances of Godot classes.

mod as_object_arg;
mod base;
mod gd;
mod guards;
mod instance_id;
mod object_arg;
mod onready;
mod raw_gd;
mod traits;

pub(crate) mod rtti;

pub use as_object_arg::*;
pub use base::*;
pub use gd::*;
pub use guards::{BaseMut, BaseRef, GdMut, GdRef};
pub use instance_id::*;
pub use object_arg::*;
pub use onready::*;
pub use raw_gd::*;
pub use traits::*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::ptr;
/// This trait is implemented for the following types:
/// - [`Gd<T>`] and `&Gd<T>`, to pass objects. Subclasses of `T` are explicitly supported.
/// - [`Option<Gd<T>>`] and `Option<&Gd<T>>`, to pass optional objects. `None` is mapped to a null argument.
/// - [`NullArg`], to pass `null` arguments without using `Option`.
/// - [`Gd::null_arg()`], to pass `null` arguments without using `Option`.
///
/// # Nullability
/// <div class="warning">
Expand Down Expand Up @@ -49,7 +49,9 @@ where
U: Inherits<T>,
{
fn as_object_arg(&self) -> ObjectArg<T> {
ObjectArg::from_raw_gd(&self.raw)
// SAFETY: In the context where as_object_arg() is called (a Godot engine call), the original Gd is guaranteed to remain valid.
// This function is not part of the public API.
unsafe { ObjectArg::from_raw_gd(&self.raw) }
}
}

Expand All @@ -64,7 +66,7 @@ where
}
}

impl<T> AsObjectArg<T> for NullArg
impl<T> AsObjectArg<T> for ObjectNullArg<T>
where
T: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
{
Expand All @@ -75,30 +77,8 @@ where

// ----------------------------------------------------------------------------------------------------------------------------------------------

/// Represents `null` when passing an object argument to Godot.
///
/// This can be used whenever a Godot signature accepts [`AsObjectArg<T>`].
/// Using `NullArg` is equivalent to passing `Option::<Gd<T>>::None`, but less wordy.
///
/// This expression is only intended for function argument lists. To work with objects that can be null, use `Option<Gd<T>>` instead.
///
/// For APIs that accept `Variant`, you can pass [`Variant::nil()`] instead.
///
/// # Nullability
/// <div class="warning">
/// The GDExtension API does not inform about nullability of its function parameters. It is up to you to verify that the arguments you pass
/// are only null when this is allowed. Doing this wrong should be safe, but can lead to the function call failing.
/// </div>
///
/// # Example
/// ```no_run
/// # fn some_shape() -> Gd<GltfPhysicsShape> { unimplemented!() }
/// use godot::prelude::*;
/// use godot_core::classes::GltfPhysicsShape;
///
/// let mut shape: Gd<GltfPhysicsShape> = some_shape();
/// shape.set_importer_mesh(NullArg);
pub struct NullArg;
#[doc(hidden)]
pub struct ObjectNullArg<T>(pub(crate) std::marker::PhantomData<*mut T>);

// ----------------------------------------------------------------------------------------------------------------------------------------------

Expand All @@ -118,7 +98,9 @@ impl<T> ObjectArg<T>
where
T: GodotClass,
{
pub fn from_raw_gd<Derived>(obj: &RawGd<Derived>) -> Self
/// # Safety
/// The referenced `RawGd` must remain valid for the lifetime of this `ObjectArg`.
pub unsafe fn from_raw_gd<Derived>(obj: &RawGd<Derived>) -> Self
where
Derived: Inherits<T>,
{
Expand Down
2 changes: 1 addition & 1 deletion godot/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub use super::global::{
pub use super::tools::{load, save, try_load, try_save, GFile};

pub use super::init::{gdextension, ExtensionLibrary, InitLevel};
pub use super::obj::{Base, Gd, GdMut, GdRef, GodotClass, Inherits, InstanceId, NullArg, OnReady};
pub use super::obj::{Base, Gd, GdMut, GdRef, GodotClass, Inherits, InstanceId, OnReady};

// Make trait methods available.
pub use super::obj::EngineBitfield as _;
Expand Down
6 changes: 3 additions & 3 deletions itest/rust/src/object_tests/object_arg_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use godot::builtin::Variant;
use godot::classes::{ClassDb, Node};
use godot::global;
use godot::obj::{Gd, NewAlloc, NullArg};
use godot::obj::{Gd, NewAlloc};

use crate::framework::itest;
use crate::object_tests::object_test::{user_refc_instance, RefcPayload};
Expand Down Expand Up @@ -71,10 +71,10 @@ fn object_arg_option_none() {
fn object_arg_null_arg() {
// Will emit errors but should not crash.
let db = ClassDb::singleton();
let error = db.class_set_property(NullArg, "name".into(), Variant::from("hello"));
let error = db.class_set_property(Gd::null_arg(), "name".into(), Variant::from("hello"));
assert_eq!(error, global::Error::ERR_UNAVAILABLE);

let error = db.class_set_property(NullArg, "value".into(), Variant::from(-123));
let error = db.class_set_property(Gd::null_arg(), "value".into(), Variant::from(-123));
assert_eq!(error, global::Error::ERR_UNAVAILABLE);
}

Expand Down

0 comments on commit 84505ba

Please sign in to comment.