Skip to content

Commit fc389f8

Browse files
committed
Add PassiveGd as clone_weak/drop_weak RAII struct
1 parent bbb5135 commit fc389f8

File tree

6 files changed

+130
-46
lines changed

6 files changed

+130
-46
lines changed

godot-core/src/obj/base.rs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use std::mem::ManuallyDrop;
1515
use std::rc::Rc;
1616

1717
use crate::builtin::{Callable, Variant};
18-
use crate::obj::{bounds, Gd, GodotClass, InstanceId};
18+
use crate::obj::{bounds, Gd, GodotClass, InstanceId, PassiveGd};
1919
use crate::{classes, sys};
2020

2121
thread_local! {
@@ -305,17 +305,16 @@ impl<T: GodotClass> Base<T> {
305305
self.obj.instance_id()
306306
}
307307

308-
/// Returns a [`Gd`] referencing the base object, for use in script contexts only.
309-
pub(crate) fn to_script_gd(&self) -> Gd<T> {
308+
/// Returns a passive reference to the base object, for use in script contexts only.
309+
pub(crate) fn to_script_passive(&self) -> PassiveGd<'_, T> {
310310
#[cfg(debug_assertions)]
311311
assert_eq!(
312312
self.init_state.get(),
313313
InitState::Script,
314-
"to_script_gd() can only be called on script-context Base objects"
314+
"to_script_passive() can only be called on script-context Base objects"
315315
);
316316

317-
// SAFETY: ScriptBase{Ref,Mut} also drop weakly.
318-
unsafe { (*self.obj).clone_weak() }
317+
PassiveGd::from_strong_ref(&self.obj)
319318
}
320319

321320
/// Returns `true` if this `Base<T>` is currently in the initializing state.
@@ -336,20 +335,35 @@ impl<T: GodotClass> Base<T> {
336335
(*self.obj).clone()
337336
}
338337

338+
/// Returns a [`PassiveGd`] referencing the base object, assuming the derived object is fully constructed.
339+
///
340+
/// This method directly creates a PassiveGd from a weak reference, providing clean lifetime management
341+
/// without the need for manual `drop_weak()` calls.
342+
pub(crate) fn constructed_passive(&self) -> PassiveGd<'_, T> {
343+
// SAFETY: returned lifetime here is re-bound to self. Covariant lifetime conversion 'static -> 'self.
344+
unsafe { self.constructed_passive_unbounded() }
345+
}
346+
339347
/// Returns a weak [`Gd`] referencing the base object, assuming the derived object is fully constructed.
340348
///
341-
/// Unlike `__constructed_gd()`, this does not increment the reference count for ref-counted `T`s.
349+
/// Unlike [`Self::__constructed_gd()`], this does not increment the reference count for ref-counted `T`s.
342350
/// The returned weak reference is safe to use only as long as the associated instance remains alive.
343-
#[doc(hidden)]
344-
pub fn __constructed_gd_weak(&self) -> Gd<T> {
351+
///
352+
/// # Safety
353+
/// This method disconnects the lifetime, as opposed to [`Self::constructed_passive()]. Caller is responsible of re-binding the
354+
/// lifetime to the instance.
355+
pub(crate) unsafe fn constructed_passive_unbounded(&self) -> PassiveGd<'static, T> {
345356
#[cfg(debug_assertions)] // debug_assert! still checks existence of symbols.
346357
assert!(
347358
!self.is_initializing(),
348359
"WithBaseField::base(), base_mut() can only be called on fully-constructed objects, after I*::init() or Gd::from_init_fn()"
349360
);
350361

351362
// Create weak reference from the same object pointer without cloning (incrementing refcount).
352-
unsafe { Gd::from_obj_sys_weak(self.obj.obj_sys()) }
363+
let weak_gd = unsafe { Gd::from_obj_sys_weak(self.obj.obj_sys()) };
364+
365+
// SAFETY: weak_gd is a weakly created Gd, and remains valid as long as self is alive (per safety precondition of this fn).
366+
unsafe { PassiveGd::from_weak_owned(weak_gd) }
353367
}
354368
}
355369

godot-core/src/obj/guards.rs

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ use godot_cell::panicking::{InaccessibleGuard, MutGuard, RefGuard};
1515
use godot_ffi::out;
1616

1717
use crate::obj::script::ScriptInstance;
18-
use crate::obj::{AsDyn, Gd, GodotClass};
18+
use crate::obj::{AsDyn, Gd, GodotClass, PassiveGd};
1919

2020
/// Immutably/shared bound reference guard for a [`Gd`][crate::obj::Gd] smart pointer.
2121
///
2222
/// See [`Gd::bind`][crate::obj::Gd::bind] for usage.
23+
// GdRef could technically implement Clone, but it wasn't needed so far.
2324
#[derive(Debug)]
2425
pub struct GdRef<'a, T: GodotClass> {
2526
guard: RefGuard<'a, T>,
@@ -45,8 +46,6 @@ impl<T: GodotClass> Drop for GdRef<'_, T> {
4546
}
4647
}
4748

48-
// TODO Clone or Share
49-
5049
// ----------------------------------------------------------------------------------------------------------------------------------------------
5150

5251
/// Mutably/exclusively bound reference guard for a [`Gd`][crate::obj::Gd] smart pointer.
@@ -199,32 +198,24 @@ macro_rules! make_base_ref {
199198
#[doc = concat!("This can be used to call methods on the base object of a ", $object_name, " that takes `&self` as the receiver.\n\n")]
200199
#[doc = concat!("See [`", stringify!($doc_type), "::base()`](", stringify!($doc_path), "::base()) for usage.")]
201200
pub struct $ident<'a, T: $bound> {
202-
// Weak reference to the base object. Safe because 'a lifetime keeps the object (strong ref) alive.
203-
// Option because Gd::drop_weak() takes ownership, thus can't use ManuallyDrop.
204-
weak_gd: Option<Gd<T::Base>>,
201+
passive_gd: PassiveGd<'a, T::Base>,
205202
_instance: &'a T,
206203
}
207204

208205
impl<'a, T: $bound> $ident<'a, T> {
209-
pub(crate) fn new(weak_gd: Gd<T::Base>, instance: &'a T) -> Self {
206+
pub(crate) fn new(passive_gd: PassiveGd<'a, T::Base>, instance: &'a T) -> Self {
210207
Self {
211-
weak_gd: Some(weak_gd),
208+
passive_gd,
212209
_instance: instance,
213210
}
214211
}
215212
}
216213

217-
impl<'a, T: $bound> Drop for $ident<'a, T> {
218-
fn drop(&mut self) {
219-
self.weak_gd.take().unwrap().drop_weak();
220-
}
221-
}
222-
223214
impl<T: $bound> Deref for $ident<'_, T> {
224215
type Target = Gd<T::Base>;
225216

226217
fn deref(&self) -> &Gd<T::Base> {
227-
self.weak_gd.as_ref().unwrap()
218+
&self.passive_gd
228219
}
229220
}
230221
};
@@ -240,41 +231,33 @@ macro_rules! make_base_mut {
240231
///
241232
#[doc = concat!("See [`", stringify!($doc_type), "::base_mut()`](", stringify!($doc_path), "::base_mut()) for usage.\n")]
242233
pub struct $ident<'a, T: $bound> {
243-
// Weak reference to the base object. Safe because 'a lifetime keeps the object (strong ref) alive.
244-
// Option because Gd::drop_weak() takes ownership, thus can't use ManuallyDrop.
245-
weak_gd: Option<Gd<T::Base>>,
234+
passive_gd: PassiveGd<'a, T::Base>,
246235
_inaccessible_guard: InaccessibleGuard<'a, T>,
247236
}
248237

249238
impl<'a, T: $bound> $ident<'a, T> {
250239
pub(crate) fn new(
251-
weak_gd: Gd<T::Base>,
240+
passive_gd: PassiveGd<'a, T::Base>,
252241
inaccessible_guard: InaccessibleGuard<'a, T>,
253242
) -> Self {
254243
Self {
255-
weak_gd: Some(weak_gd),
244+
passive_gd,
256245
_inaccessible_guard: inaccessible_guard,
257246
}
258247
}
259248
}
260249

261-
impl<'a, T: $bound> Drop for $ident<'a, T> {
262-
fn drop(&mut self) {
263-
self.weak_gd.take().unwrap().drop_weak();
264-
}
265-
}
266-
267250
impl<T: $bound> Deref for $ident<'_, T> {
268251
type Target = Gd<T::Base>;
269252

270253
fn deref(&self) -> &Gd<T::Base> {
271-
self.weak_gd.as_ref().unwrap()
254+
&self.passive_gd
272255
}
273256
}
274257

275258
impl<T: $bound> DerefMut for $ident<'_, T> {
276259
fn deref_mut(&mut self) -> &mut Gd<T::Base> {
277-
self.weak_gd.as_mut().unwrap()
260+
&mut self.passive_gd
278261
}
279262
}
280263
};

godot-core/src/obj/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ mod guards;
2020
mod instance_id;
2121
mod on_editor;
2222
mod on_ready;
23+
mod passive_gd;
2324
mod raw_gd;
2425
mod traits;
2526

@@ -33,6 +34,7 @@ pub use guards::{BaseMut, BaseRef, DynGdMut, DynGdRef, GdMut, GdRef};
3334
pub use instance_id::*;
3435
pub use on_editor::*;
3536
pub use on_ready::*;
37+
pub(crate) use passive_gd::PassiveGd;
3638
pub use raw_gd::*;
3739
pub use traits::*;
3840

godot-core/src/obj/passive_gd.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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+
8+
use std::marker::PhantomData;
9+
use std::mem::ManuallyDrop;
10+
use std::ops::{Deref, DerefMut};
11+
12+
use crate::obj::{Gd, GodotClass};
13+
14+
/// Passive (non-owning) reference to a Godot object.
15+
///
16+
/// `PassiveGd<'gd, T>` provides a safe abstraction for weak references to Godot objects. Unlike `Gd<T>`, it does not increment/decrement
17+
/// the reference count for `RefCounted` objects, and its `Drop` impl only cleans up metadata, not the Godot object.
18+
///
19+
/// The lifetime `'gd` can be used to tie it to a _strong_ `Gd<T>` reference, however it can also be `'static` if more flexibility is needed.
20+
///
21+
/// This type is primarily used internally for base object access in guards and traits, providing a clean alternative to manual
22+
/// [`Gd::clone_weak()`] and [`Gd::drop_weak()`] patterns.
23+
pub(crate) struct PassiveGd<'gd, T: GodotClass> {
24+
weak_gd: ManuallyDrop<Gd<T>>,
25+
26+
// Covariant lifetime: PassiveGd<'a, T> can be used wherever PassiveGd<'b, T> is needed, if 'a: 'b.
27+
_phantom: PhantomData<&'gd ()>,
28+
}
29+
30+
impl<'gd, T: GodotClass> PassiveGd<'gd, T> {
31+
pub fn from_strong_ref(gd: &Gd<T>) -> Self {
32+
// SAFETY:
33+
// - `clone_weak()` creates a pointer conforming to `from_weak_gd()` requirements.
34+
// - PassiveGd will destroy the pointer with `drop_weak()`.
35+
unsafe {
36+
let weak_gd = gd.clone_weak();
37+
Self::from_weak_owned(weak_gd)
38+
}
39+
}
40+
41+
/// Creates a passive reference directly from a weak `Gd<T>`.
42+
///
43+
/// Will invoke `Gd::drop_weak()` when dropped. Since the parameter has no lifetime, you need to provide the lifetime `'gd` explicitly.
44+
///
45+
/// # Safety
46+
/// - `weak_gd` must be a weakly created `Gd`, e.g. from [`Gd::clone_weak()`] or [`Gd::from_obj_sys_weak()`].
47+
/// - The caller must ensure that the `weak_gd` remains valid for the lifetime `'gd`.
48+
pub unsafe fn from_weak_owned(weak_gd: Gd<T>) -> Self {
49+
Self {
50+
weak_gd: ManuallyDrop::new(weak_gd),
51+
_phantom: PhantomData,
52+
}
53+
}
54+
}
55+
56+
impl<T: GodotClass> Drop for PassiveGd<'_, T> {
57+
fn drop(&mut self) {
58+
// SAFETY: Only extracted once, in Drop.
59+
let weak = unsafe { ManuallyDrop::take(&mut self.weak_gd) };
60+
61+
weak.drop_weak();
62+
}
63+
}
64+
65+
impl<T: GodotClass> Deref for PassiveGd<'_, T> {
66+
type Target = Gd<T>;
67+
68+
fn deref(&self) -> &Self::Target {
69+
&self.weak_gd
70+
}
71+
}
72+
73+
impl<T: GodotClass> DerefMut for PassiveGd<'_, T> {
74+
fn deref_mut(&mut self) -> &mut Self::Target {
75+
&mut self.weak_gd
76+
}
77+
}
78+
79+
// Note: We intentionally do NOT implement Clone for PassiveGd, as cloning weak references requires careful lifetime management that
80+
// should be explicit.

godot-core/src/obj/script.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,8 @@ impl<'a, T: ScriptInstance> SiMut<'a, T> {
442442
/// }
443443
/// ```
444444
pub fn base(&self) -> ScriptBaseRef<'_, T> {
445-
ScriptBaseRef::new(self.base_ref.to_script_gd(), self.mut_ref)
445+
let passive_gd = self.base_ref.to_script_passive();
446+
ScriptBaseRef::new(passive_gd, self.mut_ref)
446447
}
447448

448449
/// Returns a mutable reference suitable for calling engine methods on this object.
@@ -505,8 +506,9 @@ impl<'a, T: ScriptInstance> SiMut<'a, T> {
505506
/// ```
506507
pub fn base_mut(&mut self) -> ScriptBaseMut<'_, T> {
507508
let guard = self.cell.make_inaccessible(self.mut_ref).unwrap();
509+
let passive_gd = self.base_ref.to_script_passive();
508510

509-
ScriptBaseMut::new(self.base_ref.to_script_gd(), guard)
511+
ScriptBaseMut::new(passive_gd, guard)
510512
}
511513
}
512514

godot-core/src/obj/traits.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -422,9 +422,8 @@ pub trait WithBaseField: GodotClass + Bounds<Declarer = bounds::DeclUser> {
422422
///
423423
/// For this, use [`base_mut()`](WithBaseField::base_mut()) instead.
424424
fn base(&self) -> BaseRef<'_, Self> {
425-
let weak_gd = self.base_field().__constructed_gd_weak();
426-
427-
BaseRef::new(weak_gd, self)
425+
let passive_gd = self.base_field().constructed_passive();
426+
BaseRef::new(passive_gd, self)
428427
}
429428

430429
/// Returns a mutable reference suitable for calling engine methods on this object.
@@ -493,9 +492,12 @@ pub trait WithBaseField: GodotClass + Bounds<Declarer = bounds::DeclUser> {
493492
/// ```
494493
#[allow(clippy::let_unit_value)]
495494
fn base_mut(&mut self) -> BaseMut<'_, Self> {
496-
let weak_gd = self.base_field().__constructed_gd_weak();
495+
// We need to construct this first, as the mut-borrow below will block all other access.
496+
// SAFETY: lifetime is re-established at the bottom BaseMut construction, since return type of this fn has lifetime bound to instance.
497+
let passive_gd = unsafe { self.base_field().constructed_passive_unbounded() };
497498

498499
let gd = self.to_gd();
500+
499501
// SAFETY:
500502
// - We have a `Gd<Self>` so, provided that `storage_unbounded` succeeds, the associated instance
501503
// storage has been created.
@@ -509,12 +511,13 @@ pub trait WithBaseField: GodotClass + Bounds<Declarer = bounds::DeclUser> {
509511
let storage = unsafe {
510512
gd.raw
511513
.storage_unbounded()
512-
.expect("we have a `Gd<Self>` so the raw should not be null")
514+
.expect("we have Gd<Self>; its RawGd should not be null")
513515
};
514516

515517
let guard = storage.get_inaccessible(self);
516518

517-
BaseMut::new(weak_gd, guard)
519+
// Narrows lifetime again from 'static to 'self.
520+
BaseMut::new(passive_gd, guard)
518521
}
519522
}
520523

0 commit comments

Comments
 (0)