Skip to content

Commit 6e0f41c

Browse files
authored
Merge pull request #1171 from godot-rust/qol/signal-polish
Clean up `ConnectBuilder` and some other signal APIs
2 parents 0e40b4d + 90bae8b commit 6e0f41c

File tree

8 files changed

+321
-185
lines changed

8 files changed

+321
-185
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ where
7272
/// once the reference is returned. Could use more fancy syntax like `arg_into_ref! { let path = ref; }` or `let path = arg_into_ref!(path)`,
7373
/// but still isn't obvious enough to avoid doc lookup and might give a wrong idea about the scope. So being more exotic is a feature.
7474
#[macro_export]
75+
#[doc(hidden)] // Doesn't work at re-export.
7576
macro_rules! arg_into_ref {
7677
($arg_variable:ident) => {
7778
// Non-generic version allows type inference. Only applicable for CowArg types.
@@ -88,6 +89,7 @@ macro_rules! arg_into_ref {
8889
///
8990
/// A macro for consistency with [`arg_into_ref`][crate::arg_into_ref].
9091
#[macro_export]
92+
#[doc(hidden)] // Doesn't work at re-export.
9193
macro_rules! arg_into_owned {
9294
($arg_variable:ident) => {
9395
// Non-generic version allows type inference. Only applicable for CowArg types.

godot-core/src/meta/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ mod param_tuple;
5252
mod property_info;
5353
mod signature;
5454
mod traits;
55+
mod uniform_object_deref;
5556

5657
pub(crate) mod sealed;
5758

@@ -60,9 +61,9 @@ pub mod error;
6061
pub use args::*;
6162
pub use class_name::ClassName;
6263
pub use godot_convert::{FromGodot, GodotConvert, ToGodot};
63-
pub use traits::{ArrayElement, GodotType, PackedArrayElement};
64-
6564
pub use param_tuple::{InParamTuple, OutParamTuple, ParamTuple};
65+
pub use traits::{ArrayElement, GodotType, PackedArrayElement};
66+
pub use uniform_object_deref::UniformObjectDeref;
6667

6768
pub(crate) use array_type_info::ArrayTypeInfo;
6869
pub(crate) use traits::{
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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 crate::obj::bounds::{DeclEngine, DeclUser};
9+
use crate::obj::{Gd, GdMut, GdRef, GodotClass, WithBaseField};
10+
use std::ops::{Deref, DerefMut};
11+
12+
/// Unifies dereferencing of user and engine classes, as `&T`/`&mut T` and `Gd<T>`.
13+
///
14+
/// This is mainly used by the `connect_*` functions of [`TypedSignal`](crate::registry::signal::TypedSignal).
15+
///
16+
/// # Motivation
17+
/// Although both user and engine classes are often wrapped in a `Gd<T>`, dereferencing them is done differently depending
18+
/// on whether they are made by the user or engine:
19+
/// - `Gd<EngineClass>` can be deref-ed directly into `&EngineClass` and `&mut EngineClass`.
20+
/// - `Gd<UserClass>` must first go through [`bind()`](Gd::bind)/[`bind_mut()`](Gd::bind_mut), which can finally
21+
/// be deref-ed into `&UserClass` and `&mut UserClass`, respectively.
22+
///
23+
/// Without this trait, there's no clear/generic way of writing functions that can accept both user and engine classes,
24+
/// but need to deref said classes in some way.
25+
///
26+
/// [`UniformObjectDeref`](Self) solves this by explicitly handling each category in a different way, but still resulting
27+
/// in a variable that can be deref-ed into `&T`/`&mut T`.
28+
///
29+
/// # Generic param `Declarer`
30+
/// Rustc [does not acknowledge associated type bounds when checking for overlapping impls](https://github.com/rust-lang/rust/issues/20400),
31+
/// this parameter is essentially used to create 2 different traits, one for each "category" (user or engine).
32+
///
33+
/// Despite being 2 different traits, a function can accept both by simply being generic over `Declarer`:
34+
/// ```no_run
35+
/// use godot::meta::UniformObjectDeref;
36+
/// # use godot::prelude::*;
37+
///
38+
/// fn abstract_over_objects<Declarer, C>(obj: &Gd<C>)
39+
/// where
40+
/// C: UniformObjectDeref<Declarer>,
41+
/// {
42+
/// let ref_provider = UniformObjectDeref::object_as_ref(obj);
43+
/// let obj_ref: &C = & *ref_provider;
44+
/// // Regardless of `Declarer`, we can still deref, since the bounds on
45+
/// // `TargetRef`/`TargetMut` enforce that.
46+
/// }
47+
///
48+
/// #[derive(GodotClass)]
49+
/// #[class(init)]
50+
/// struct MyClass {
51+
/// _base: Base<RefCounted>
52+
/// }
53+
///
54+
/// fn main() {
55+
/// let engine_obj: Gd<RefCounted> = RefCounted::new_gd();
56+
/// let user_obj: Gd<MyClass> = MyClass::new_gd();
57+
///
58+
/// abstract_over_objects(&engine_obj);
59+
/// abstract_over_objects(&user_obj);
60+
/// }
61+
/// ```
62+
//
63+
// The crate `https://crates.io/crates/disjoint_impls` handles this in a more user-friendly way, we should
64+
// consider using it if disjoint impls are going to be frequently used.
65+
//
66+
// See also Declarer::DerefTarget in the library, which has a similar but different purpose: finding the nearest engine class
67+
// (`&Node` for `Node`, `&Node` for `MyClass`).
68+
#[allow(clippy::needless_lifetimes)] // False positive.
69+
pub trait UniformObjectDeref<Declarer>: GodotClass {
70+
// Currently, only the mut parts are used within the library; but ref might be useful too.
71+
type TargetRef<'a>: Deref<Target = Self>;
72+
type TargetMut<'a>: DerefMut<Target = Self>;
73+
74+
fn object_as_ref<'a>(gd: &'a Gd<Self>) -> Self::TargetRef<'a>;
75+
fn object_as_mut<'a>(gd: &'a mut Gd<Self>) -> Self::TargetMut<'a>;
76+
}
77+
78+
#[allow(clippy::needless_lifetimes)] // False positive.
79+
impl<T: GodotClass<Declarer = DeclEngine>> UniformObjectDeref<DeclEngine> for T {
80+
type TargetRef<'a> = Gd<T>;
81+
type TargetMut<'a> = Gd<T>;
82+
83+
fn object_as_ref<'a>(gd: &'a Gd<Self>) -> Self::TargetRef<'a> {
84+
gd.clone()
85+
}
86+
fn object_as_mut<'a>(gd: &'a mut Gd<Self>) -> Self::TargetMut<'a> {
87+
gd.clone()
88+
}
89+
}
90+
91+
#[allow(clippy::needless_lifetimes)] // False positive.
92+
impl<T: WithBaseField> UniformObjectDeref<DeclUser> for T {
93+
type TargetRef<'a> = GdRef<'a, T>;
94+
type TargetMut<'a> = GdMut<'a, T>;
95+
96+
fn object_as_ref<'a>(gd: &'a Gd<Self>) -> Self::TargetRef<'a> {
97+
gd.bind()
98+
}
99+
fn object_as_mut<'a>(gd: &'a mut Gd<Self>) -> Self::TargetMut<'a> {
100+
gd.bind_mut()
101+
}
102+
}

0 commit comments

Comments
 (0)