Skip to content

Commit d48db7c

Browse files
committed
Docs + cleanup
Changes: * Rename dbind() -> dyn_bind(). * DynGd::from_gd() should now be used as gd.into_dyn(). * Add test for Godot method call (through Deref/DerefMut).
1 parent 8176c01 commit d48db7c

File tree

5 files changed

+92
-50
lines changed

5 files changed

+92
-50
lines changed

godot-core/src/meta/args/object_arg.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use crate::builtin::Variant;
99
use crate::meta::error::ConvertError;
1010
use crate::meta::{ClassName, FromGodot, GodotConvert, GodotFfiVariant, GodotType, ToGodot};
11-
use crate::obj::{bounds, Bounds, Gd, GodotClass, Inherits, RawGd};
11+
use crate::obj::{bounds, Bounds, DynGd, Gd, GodotClass, Inherits, RawGd};
1212
use crate::{obj, sys};
1313
use godot_ffi::{GodotFfi, GodotNullableFfi, PtrcallType};
1414
use std::ptr;
@@ -81,6 +81,7 @@ where
8181
}
8282
}
8383
*/
84+
8485
impl<T, U> AsObjectArg<T> for &Gd<U>
8586
where
8687
T: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
@@ -97,6 +98,25 @@ where
9798
}
9899
}
99100

101+
impl<T, U, D> AsObjectArg<T> for &DynGd<U, D>
102+
where
103+
T: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
104+
U: Inherits<T>,
105+
D: ?Sized,
106+
{
107+
fn as_object_arg(&self) -> ObjectArg<T> {
108+
// Reuse Deref.
109+
let gd: &Gd<U> = self;
110+
<&Gd<U>>::as_object_arg(&gd)
111+
}
112+
113+
fn consume_arg(self) -> ObjectCow<T> {
114+
// Reuse Deref.
115+
let gd: &Gd<U> = self;
116+
<&Gd<U>>::consume_arg(gd)
117+
}
118+
}
119+
100120
impl<T, U> AsObjectArg<T> for Option<U>
101121
where
102122
T: GodotClass + Bounds<Declarer = bounds::DeclEngine>,

godot-core/src/obj/dyn_gd.rs

+22-16
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@ use std::ops;
1919
/// To register the `T` -> `D` relation with godot-rust, `T` must implement [`AsDyn<D>`]. This can be automated with the
2020
/// [`#[godot_dyn]`](../register/attr.godot_dyn.html) attribute macro.
2121
///
22-
/// # Public API
23-
/// The API is very close to `Gd`. In fact, both `Deref` and `DerefMut` are implemented for `DynGd` -> `Gd`, so you can access all the
22+
/// # Construction and API
23+
/// You can convert between `Gd` and `DynGd` using [`Gd::into_dyn()`] and [`DynGd::into_gd()`]. The former sometimes needs an explicit
24+
/// `::<dyn Trait>` type argument, but can often be inferred.
25+
///
26+
/// The `DynGd` API is very close to `Gd`. In fact, both `Deref` and `DerefMut` are implemented for `DynGd` -> `Gd`, so you can access all the
2427
/// underlying `Gd` methods as well as Godot class APIs directly.
2528
///
26-
/// The main new parts are two methods [`dbind()`][Self::dbind] and [`dbind_mut()`][Self::dbind_mut]. These are very similar to `Gd`'s
29+
/// The main new parts are two methods [`dyn_bind()`][Self::dyn_bind] and [`dyn_bind_mut()`][Self::dyn_bind_mut]. These are very similar to `Gd`'s
2730
/// [`bind()`][Gd::bind] and [`bind_mut()`][Gd::bind_mut], but return a reference guard to the trait object `D` instead of the Godot class `T`.
2831
///
2932
/// # Example
@@ -66,11 +69,11 @@ use std::ops;
6669
/// let mut dyn_monster = dyn_monster.upcast::<RefCounted>();
6770
///
6871
/// // Due to RefCounted abstraction, you can no longer access concrete Monster properties.
69-
/// // However, the trait Health is still accessible through dbind().
70-
/// assert!(dyn_monster.dbind().is_alive());
72+
/// // However, the trait Health is still accessible through dyn_bind().
73+
/// assert!(dyn_monster.dyn_bind().is_alive());
7174
///
72-
/// // To mutate the object, call dbind_mut(). Rust borrow rules apply.
73-
/// let mut guard = dyn_monster.dbind_mut();
75+
/// // To mutate the object, call dyn_bind_mut(). Rust borrow rules apply.
76+
/// let mut guard = dyn_monster.dyn_bind_mut();
7477
/// guard.deal_damage(120);
7578
/// assert!(!guard.is_alive());
7679
/// ```
@@ -90,7 +93,7 @@ where
9093
T: AsDyn<D> + Bounds<Declarer = bounds::DeclUser>,
9194
D: ?Sized,
9295
{
93-
pub fn from_gd(gd_instance: Gd<T>) -> Self {
96+
pub(crate) fn from_gd(gd_instance: Gd<T>) -> Self {
9497
let erased_obj = Box::new(gd_instance.clone());
9598

9699
Self {
@@ -111,17 +114,17 @@ where
111114
/// The resulting guard implements `Deref<Target = D>`, allowing shared access to the trait's methods.
112115
///
113116
/// See [`Gd::bind()`][Gd::bind] for borrow checking semantics and panics.
114-
pub fn dbind(&self) -> DynGdRef<D> {
115-
self.erased_obj.dbind()
117+
pub fn dyn_bind(&self) -> DynGdRef<D> {
118+
self.erased_obj.dyn_bind()
116119
}
117120

118121
/// Acquires an exclusive reference guard to the trait object `D`.
119122
///
120123
/// The resulting guard implements `DerefMut<Target = D>`, allowing exclusive mutable access to the trait's methods.
121124
///
122125
/// See [`Gd::bind_mut()`][Gd::bind_mut] for borrow checking semantics and panics.
123-
pub fn dbind_mut(&mut self) -> DynGdMut<D> {
124-
self.erased_obj.dbind_mut()
126+
pub fn dyn_bind_mut(&mut self) -> DynGdMut<D> {
127+
self.erased_obj.dyn_bind_mut()
125128
}
126129

127130
// Certain methods "overridden" from deref'ed Gd here, so they're more idiomatic to use.
@@ -201,8 +204,8 @@ where
201204
// Type erasure
202205

203206
trait ErasedGd<D: ?Sized> {
204-
fn dbind(&self) -> DynGdRef<D>;
205-
fn dbind_mut(&mut self) -> DynGdMut<D>;
207+
fn dyn_bind(&self) -> DynGdRef<D>;
208+
fn dyn_bind_mut(&mut self) -> DynGdMut<D>;
206209

207210
fn clone_box(&self) -> Box<dyn ErasedGd<D>>;
208211
}
@@ -212,15 +215,18 @@ where
212215
T: AsDyn<D> + Bounds<Declarer = bounds::DeclUser>,
213216
D: ?Sized,
214217
{
215-
fn dbind(&self) -> DynGdRef<D> {
218+
fn dyn_bind(&self) -> DynGdRef<D> {
216219
DynGdRef::from_guard::<T>(Gd::bind(self))
217220
}
218221

219-
fn dbind_mut(&mut self) -> DynGdMut<D> {
222+
fn dyn_bind_mut(&mut self) -> DynGdMut<D> {
220223
DynGdMut::from_guard::<T>(Gd::bind_mut(self))
221224
}
222225

223226
fn clone_box(&self) -> Box<dyn ErasedGd<D>> {
224227
Box::new(Gd::clone(self))
225228
}
226229
}
230+
231+
// ----------------------------------------------------------------------------------------------------------------------------------------------
232+
// Integration with Godot traits

godot-core/src/obj/gd.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,11 @@ impl<T: GodotClass> Gd<T> {
431431
}
432432
}
433433

434+
/// Upgrades to a `DynGd<T, D>` pointer, enabling the `D` abstraction.
435+
///
436+
/// The `D` parameter can typically be inferred when there is a single `AsDyn<...>` implementation for `T`. \
437+
/// Otherwise, use it as `gd.into_dyn::<dyn MyTrait>()`.
438+
#[must_use]
434439
pub fn into_dyn<D>(self) -> DynGd<T, D>
435440
where
436441
T: crate::obj::AsDyn<D> + Bounds<Declarer = bounds::DeclUser>,
@@ -702,15 +707,18 @@ impl<T: GodotClass> FromGodot for Gd<T> {
702707
}
703708

704709
impl<T: GodotClass> GodotType for Gd<T> {
710+
// Some #[doc(hidden)] are repeated despite already declared in trait; some IDEs suggest in auto-complete otherwise.
705711
type Ffi = RawGd<T>;
706712

707713
type ToFfi<'f> = RefArg<'f, RawGd<T>>
708714
where Self: 'f;
709715

716+
#[doc(hidden)]
710717
fn to_ffi(&self) -> Self::ToFfi<'_> {
711718
RefArg::new(&self.raw)
712719
}
713720

721+
#[doc(hidden)]
714722
fn into_ffi(self) -> Self::Ffi {
715723
self.raw
716724
}
@@ -723,7 +731,7 @@ impl<T: GodotClass> GodotType for Gd<T> {
723731
}
724732
}
725733

726-
fn class_name() -> crate::meta::ClassName {
734+
fn class_name() -> ClassName {
727735
T::class_name()
728736
}
729737

@@ -781,6 +789,7 @@ impl<T: GodotClass> ArrayElement for Option<Gd<T>> {
781789
}
782790

783791
impl<'r, T: GodotClass> AsArg<Gd<T>> for &'r Gd<T> {
792+
#[doc(hidden)] // Repeated despite already hidden in trait; some IDEs suggest this otherwise.
784793
fn into_arg<'cow>(self) -> CowArg<'cow, Gd<T>>
785794
where
786795
'r: 'cow, // Original reference must be valid for at least as long as the returned cow.

godot-core/src/obj/guards.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl<'a, T: GodotClass> ErasedGuard<'a> for GdMut<'a, T> {}
9797

9898
/// Shared reference guard for a [`DynGd`][crate::obj::DynGd] smart pointer.
9999
///
100-
/// Returned by [`DynGd::dbind()`][crate::obj::DynGd::dbind].
100+
/// Returned by [`DynGd::dyn_bind()`][crate::obj::DynGd::dyn_bind].
101101
pub struct DynGdRef<'a, D: ?Sized> {
102102
/// Never accessed, but is kept alive to ensure dynamic borrow checks are upheld and the object isn't freed.
103103
_guard: Box<dyn ErasedGuard<'a>>,
@@ -139,7 +139,7 @@ impl<D: ?Sized> Drop for DynGdRef<'_, D> {
139139

140140
/// Mutably/exclusively bound reference guard for a [`DynGd`][crate::obj::DynGd] smart pointer.
141141
///
142-
/// Returned by [`DynGd::dbind_mut()`][crate::obj::DynGd::dbind_mut].
142+
/// Returned by [`DynGd::dyn_bind_mut()`][crate::obj::DynGd::dyn_bind_mut].
143143
pub struct DynGdMut<'a, D: ?Sized> {
144144
/// Never accessed, but is kept alive to ensure dynamic borrow checks are upheld and the object isn't freed.
145145
_guard: Box<dyn ErasedGuard<'a>>,

itest/rust/src/object_tests/dyn_gd_test.rs

+37-30
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,35 @@
44
* License, v. 2.0. If a copy of the MPL was not distributed with this
55
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
66
*/
7-
87
use crate::framework::{expect_panic, itest};
98
// Test that all important dyn-related symbols are in the prelude.
109
use godot::prelude::*;
1110

1211
#[itest]
1312
fn dyn_gd_creation_bind() {
1413
// Type inference on this works because only 1 AsDyn<...> trait is implemented for RefcHealth. It would fail with another.
15-
let _unused = DynGd::from_gd(Gd::from_object(RefcHealth { hp: 1 }));
14+
let _unused = Gd::from_object(RefcHealth { hp: 1 }).into_dyn();
1615

1716
let user_obj = RefcHealth { hp: 34 };
18-
let mut dyn_gd = DynGd::from_gd(Gd::from_object(user_obj));
17+
let mut dyn_gd = Gd::from_object(user_obj).into_dyn();
1918

2019
{
2120
// Exclusive bind.
2221
// Interesting: this can be type inferred because RefcHealth implements only 1 AsDyn<...> trait.
2322
// If there were another, type inference would fail.
24-
let mut health = dyn_gd.dbind_mut();
23+
let mut health = dyn_gd.dyn_bind_mut();
2524
health.deal_damage(4);
2625
}
2726
{
2827
// Test multiple shared binds.
29-
let health_a = dyn_gd.dbind();
30-
let health_b = dyn_gd.dbind();
28+
let health_a = dyn_gd.dyn_bind();
29+
let health_b = dyn_gd.dyn_bind();
3130

3231
assert_eq!(health_b.get_hitpoints(), 30);
3332
assert_eq!(health_a.get_hitpoints(), 30);
3433
}
3534
{
36-
let mut health = dyn_gd.dbind_mut();
35+
let mut health = dyn_gd.dyn_bind_mut();
3736
health.kill();
3837

3938
assert_eq!(health.get_hitpoints(), 0);
@@ -45,15 +44,13 @@ fn dyn_gd_creation_deref() {
4544
let node = foreign::NodeHealth::new_alloc();
4645
let original_id = node.instance_id();
4746

48-
// let mut node = node.into_dyn::<dyn Health>();
49-
// The first line only works because both type parameters are inferred as RefcHealth, and there's no `dyn Health`.
50-
let mut node = DynGd::from_gd(node);
47+
let mut node = node.into_dyn::<dyn Health>();
5148

5249
let dyn_id = node.instance_id();
5350
assert_eq!(dyn_id, original_id);
5451

55-
deal_20_damage(&mut *node.dbind_mut());
56-
assert_eq!(node.dbind().get_hitpoints(), 80);
52+
deal_20_damage(&mut *node.dyn_bind_mut());
53+
assert_eq!(node.dyn_bind().get_hitpoints(), 80);
5754

5855
node.free();
5956
}
@@ -72,40 +69,40 @@ fn dyn_gd_upcast() {
7269
let mut node = concrete.clone().upcast::<Node>();
7370
let object = concrete.upcast::<Object>();
7471

75-
node.dbind_mut().deal_damage(25);
72+
node.dyn_bind_mut().deal_damage(25);
7673

7774
// Make sure identity is intact.
7875
assert_eq!(node.instance_id(), original_copy.instance_id());
7976

80-
// Ensure applied to the object polymorphically. Concrete object can access bind(), no dbind().
77+
// Ensure applied to the object polymorphically. Concrete object can access bind(), no dyn_bind().
8178
assert_eq!(original_copy.bind().get_hitpoints(), 75);
8279

83-
// Check also another angle (via Object). Here dbind().
84-
assert_eq!(object.dbind().get_hitpoints(), 75);
80+
// Check also another angle (via Object). Here dyn_bind().
81+
assert_eq!(object.dyn_bind().get_hitpoints(), 75);
8582

8683
node.free();
8784
}
8885

8986
#[itest]
9087
fn dyn_gd_exclusive_guard() {
91-
let mut a = DynGd::from_gd(foreign::NodeHealth::new_alloc());
88+
let mut a = foreign::NodeHealth::new_alloc().into_dyn();
9289
let mut b = a.clone();
9390

94-
let guard = a.dbind_mut();
91+
let guard = a.dyn_bind_mut();
9592

9693
expect_panic(
97-
"Cannot acquire dbind() guard while dbind_mut() is held",
94+
"Cannot acquire dyn_bind() guard while dyn_bind_mut() is held",
9895
|| {
99-
let _ = b.dbind();
96+
let _ = b.dyn_bind();
10097
},
10198
);
10299
expect_panic(
103-
"Cannot acquire 2nd dbind_mut() guard while dbind_mut() is held",
100+
"Cannot acquire 2nd dyn_bind_mut() guard while dyn_bind_mut() is held",
104101
|| {
105-
let _ = b.dbind_mut();
102+
let _ = b.dyn_bind_mut();
106103
},
107104
);
108-
expect_panic("Cannot free object while dbind_mut() is held", || {
105+
expect_panic("Cannot free object while dyn_bind_mut() is held", || {
109106
b.free();
110107
});
111108

@@ -115,26 +112,26 @@ fn dyn_gd_exclusive_guard() {
115112

116113
#[itest]
117114
fn dyn_gd_shared_guard() {
118-
let a = DynGd::from_gd(foreign::NodeHealth::new_alloc());
115+
let a = foreign::NodeHealth::new_alloc().into_dyn();
119116
let b = a.clone();
120117
let mut c = a.clone();
121118

122-
let guard_a = a.dbind();
119+
let guard_a = a.dyn_bind();
123120

124-
// CAN acquire another dbind() while an existing one exists.
125-
let guard_b = b.dbind();
121+
// CAN acquire another dyn_bind() while an existing one exists.
122+
let guard_b = b.dyn_bind();
126123
drop(guard_a);
127124

128125
// guard_b still alive here.
129126
expect_panic(
130-
"Cannot acquire dbind_mut() guard while dbind() is held",
127+
"Cannot acquire dyn_bind_mut() guard while dyn_bind() is held",
131128
|| {
132-
let _ = c.dbind_mut();
129+
let _ = c.dyn_bind_mut();
133130
},
134131
);
135132

136133
// guard_b still alive here.
137-
expect_panic("Cannot free object while dbind() is held", || {
134+
expect_panic("Cannot free object while dyn_bind() is held", || {
138135
c.free();
139136
});
140137

@@ -153,6 +150,16 @@ fn dyn_gd_downgrade() {
153150
assert_eq!(gd.instance_id(), dyn_id);
154151
}
155152

153+
#[itest]
154+
fn dyn_gd_call_godot_method() {
155+
let mut node = foreign::NodeHealth::new_alloc().into_dyn();
156+
157+
node.set_name("dyn-name!");
158+
assert_eq!(node.get_name(), "dyn-name!".into());
159+
160+
node.free();
161+
}
162+
156163
#[itest]
157164
fn dyn_gd_pass_to_godot_api() {
158165
let child = foreign::NodeHealth::new_alloc().into_dyn();

0 commit comments

Comments
 (0)