Skip to content

Commit db2173f

Browse files
committed
Implement support for flexible self types
`Instance` and `TInstance` can now be used as self types where applicable. Additionally, `Arc` is supported when `ArcData` is used as the user-data wrapper. The `self` type can be provided through the standard `arbitrary_self_types` syntax, or a `#[self]` attribute on the argument when unavailable.
1 parent 11466c8 commit db2173f

File tree

6 files changed

+326
-64
lines changed

6 files changed

+326
-64
lines changed

gdnative-core/src/object/instance.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::fmt::Debug;
12
use std::ptr::NonNull;
23

34
use crate::core_types::{
@@ -13,6 +14,9 @@ use crate::object::ownership::{NonUniqueOwnership, Ownership, Shared, ThreadLoca
1314
use crate::object::{GodotObject, Instanciable, QueueFree, RawObject, Ref, TRef};
1415
use crate::private::{get_api, ReferenceCountedClassPlaceholder};
1516

17+
mod receiver;
18+
19+
pub use receiver::Receiver;
1620
/// A persistent reference to a GodotObject with a rust NativeClass attached.
1721
///
1822
/// `Instance`s can be worked on directly using `map` and `map_mut` if the base object is
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
use std::fmt::{Debug, Display};
2+
use std::sync::Arc;
3+
4+
use crate::export::user_data::{ArcData, Map, MapMut, MapOwned};
5+
use crate::export::NativeClass;
6+
use crate::object::ownership::Shared;
7+
8+
use super::{Instance, TInstance};
9+
10+
/// Trait for types that can be used as the `self` or `#[self]` argument (receiver) for methods
11+
/// exported through the `#[methods]` attribute macro. This trait has no public interface, and
12+
/// is not intended to be implemented by users.
13+
///
14+
/// Notably, this is implemented for [`Instance`] and [`TInstance`] along with the usual `self`
15+
/// reference types. For types using [`ArcData`] as the wrapper specifically, [`Arc<C>`] is also
16+
/// allowed.
17+
///
18+
/// The trait is unsealed for technical coherence issues, but is not intended to be implemented
19+
/// by users. Changes to the definition of this trait are not considered breaking changes under
20+
/// semver.
21+
pub trait Receiver<C: NativeClass>: Sized {
22+
#[doc(hidden)]
23+
type This<'a>;
24+
#[doc(hidden)]
25+
type Err: Debug;
26+
27+
#[doc(hidden)]
28+
fn with_instance<F, R>(instance: TInstance<'_, C, Shared>, f: F) -> Result<R, Self::Err>
29+
where
30+
F: for<'a> FnOnce(Self::This<'a>) -> R;
31+
}
32+
33+
/// Error type indicating that an operation can't fail.
34+
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
35+
#[allow(clippy::exhaustive_enums)] // explicitly uninhabited
36+
pub enum Infallible {}
37+
38+
impl Display for Infallible {
39+
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40+
unreachable!("uninhabited enum")
41+
}
42+
}
43+
44+
impl<'r, C: NativeClass> Receiver<C> for TInstance<'r, C, Shared> {
45+
type This<'a> = TInstance<'a, C, Shared>;
46+
type Err = Infallible;
47+
48+
#[inline]
49+
fn with_instance<F, R>(instance: TInstance<'_, C, Shared>, f: F) -> Result<R, Self::Err>
50+
where
51+
F: for<'a> FnOnce(Self::This<'a>) -> R,
52+
{
53+
Ok(f(instance))
54+
}
55+
}
56+
57+
impl<C: NativeClass> Receiver<C> for Instance<C, Shared> {
58+
type This<'a> = Instance<C, Shared>;
59+
type Err = Infallible;
60+
61+
#[inline]
62+
fn with_instance<F, R>(instance: TInstance<'_, C, Shared>, f: F) -> Result<R, Self::Err>
63+
where
64+
F: for<'a> FnOnce(Self::This<'a>) -> R,
65+
{
66+
Ok(f(instance.claim()))
67+
}
68+
}
69+
70+
impl<'r, C: NativeClass> Receiver<C> for &'r C
71+
where
72+
C::UserData: Map,
73+
{
74+
type This<'a> = &'a C;
75+
type Err = <C::UserData as Map>::Err;
76+
77+
#[inline]
78+
fn with_instance<F, R>(instance: TInstance<'_, C, Shared>, f: F) -> Result<R, Self::Err>
79+
where
80+
F: for<'a> FnOnce(Self::This<'a>) -> R,
81+
{
82+
instance.map(|this, _| f(this))
83+
}
84+
}
85+
86+
impl<'r, C: NativeClass> Receiver<C> for &'r mut C
87+
where
88+
C::UserData: MapMut,
89+
{
90+
type This<'a> = &'a mut C;
91+
type Err = <C::UserData as MapMut>::Err;
92+
93+
#[inline]
94+
fn with_instance<F, R>(instance: TInstance<'_, C, Shared>, f: F) -> Result<R, Self::Err>
95+
where
96+
F: for<'a> FnOnce(Self::This<'a>) -> R,
97+
{
98+
instance.map_mut(|this, _| f(this))
99+
}
100+
}
101+
102+
impl<C: NativeClass> Receiver<C> for C
103+
where
104+
C::UserData: MapOwned,
105+
{
106+
type This<'a> = C;
107+
type Err = <C::UserData as MapOwned>::Err;
108+
109+
#[inline]
110+
fn with_instance<F, R>(instance: TInstance<'_, C, Shared>, f: F) -> Result<R, Self::Err>
111+
where
112+
F: for<'a> FnOnce(Self::This<'a>) -> R,
113+
{
114+
instance.map_owned(|this, _| f(this))
115+
}
116+
}
117+
118+
impl<C> Receiver<C> for Arc<C>
119+
where
120+
C: NativeClass<UserData = ArcData<C>>,
121+
{
122+
type This<'a> = Arc<C>;
123+
type Err = Infallible;
124+
125+
#[inline]
126+
fn with_instance<F, R>(instance: TInstance<'_, C, Shared>, f: F) -> Result<R, Self::Err>
127+
where
128+
F: for<'a> FnOnce(Self::This<'a>) -> R,
129+
{
130+
let (_, script) = instance.claim().decouple();
131+
Ok(f(script.into_inner()))
132+
}
133+
}

gdnative-derive/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,13 @@ pub fn profiled(meta: TokenStream, input: TokenStream) -> TokenStream {
289289
/// [exporting](https://docs.godotengine.org/en/stable/tutorials/export/exporting_basics.html) in GDScript.
290290
///
291291
/// A valid function signature must have:
292-
/// - `self`, `&self` or `&mut self` as its first parameter, if applicable.
292+
/// - Up to one receiver parameter as the first parameter. This can be one of:
293+
/// - `self`, `&self` or `&mut self`.
294+
/// - `self: Instance<Self>` or `self: TInstance<Self>`, when the `arbitrary_self_types` feature
295+
/// is available. Additionally, `self: Arc<Self>` is allowed when the `user_data` wrapper is
296+
/// specified to be `ArcData<Self>`.
297+
/// - `#[self] this: T` where `T` is any of the types mentioned above, as a workaround when
298+
/// `arbitrary_self_types` is unavailable.
293299
/// - Up of one of each of the following special arguments, in any order, denoted by the attributes:
294300
/// - `#[base]` - A reference to the base/owner object. This may be `&T` or `TRef<T>`m where `T` refers to
295301
/// the type declared in `#[inherit(T)]` attribute for the `NativeClass` type.
@@ -307,6 +313,10 @@ pub fn profiled(meta: TokenStream, input: TokenStream) -> TokenStream {
307313
/// #[method]
308314
/// fn foo();
309315
///
316+
/// // Access `TInstance` instead of a variation of `self`, without `arbitrary_self_types`.
317+
/// #[method]
318+
/// fn foo(#[self] this: TInstance<Self>);
319+
///
310320
/// // No access to base parameter
311321
/// #[method]
312322
/// fn foo(&self);

0 commit comments

Comments
 (0)