@@ -24,23 +24,64 @@ use std::marker::PhantomData;
24
24
/// - `&mut C` for `&mut self` methods.
25
25
///
26
26
/// See also [Signals](https://godot-rust.github.io/book/register/signals.html) in the book.
27
+ ///
28
+ /// # Usage as a bound
29
+ /// To write generic code that handles different functions and forwards them to [`TypedSignal`] and [`ConnectBuilder`] APIs,
30
+ /// you have to use `SignalReceiver` in combination with [`IndirectSignalReceiver`]. Consult the docs of the latter for elaboration.
31
+ ///
32
+ /// [`TypedSignal`]: crate::registry::signal::TypedSignal
33
+ /// [`ConnectBuilder`]: crate::registry::signal::ConnectBuilder
34
+ ///
35
+ /// # Hidden `impls` in this doc
36
+ /// To keep this documentation readable, we only document one variant of each `impl SignalReceiver`: arbitrarily the one with three parameters
37
+ /// `(P0, P1, P2)`. Keep this in mind when looking at a concrete signature.
27
38
pub trait SignalReceiver < C , Ps > : ' static {
28
39
/// Invoke the receiver on the given instance (possibly `()`) with `params`.
29
40
fn call ( & mut self , maybe_instance : C , params : Ps ) ;
30
41
}
31
42
32
43
// Next-gen trait solver should allow for type inference in closures, when function traits are involved, without using identity struct
33
44
// and other hacks. Since `IndirectSignalReceiver` is just a view, it should be drop-in replacement.
34
- /// A special "identity struct" which allows to use type inference while specifying various closures for connections.
45
+ /// A special "identity struct" which enables type inference while specifying various closures for connections.
46
+ ///
47
+ /// If you want to write generic code that can handle different functions and forward them to [`TypedSignal`] and [`ConnectBuilder`] APIs,
48
+ /// you have to use [`SignalReceiver`] in combination with this type. Please check its documentation for a detailed explanation.
49
+ ///
50
+ /// [`TypedSignal`]: crate::registry::signal::TypedSignal
51
+ /// [`ConnectBuilder`]: crate::registry::signal::ConnectBuilder
52
+ ///
53
+ /// # Background
54
+ ///
55
+ /// Individual `connect*` methods on `TypedSignal` and `ConnectBuilder` use the `SignalReceiver` trait as a bound. \
56
+ /// **In addition to that**, they have a rather complex bound involving [`IndirectSignalReceiver`]:
57
+ ///
58
+ /// ```no_run
59
+ /// # use godot::register::{IndirectSignalReceiver, SignalReceiver};
60
+ /// # use godot_core::meta::InParamTuple;
61
+ /// # use godot_core::obj::WithSignals;
62
+ /// # struct TypedSignal<'c, C: WithSignals, Ps> { _phantom: std::marker::PhantomData<&'c (C, Ps)> }
63
+ /// impl<C: WithSignals, Ps: InParamTuple + 'static> TypedSignal<'_, C, Ps> {
64
+ /// pub fn connect<F>(&self, mut function: F)
65
+ /// where
66
+ /// for<'c_rcv> F: SignalReceiver<(), Ps> + 'static,
67
+ /// for<'c_rcv> IndirectSignalReceiver<'c_rcv, (), Ps, F>: From<&'c_rcv mut F>,
68
+ /// { /* ... */ }
69
+ /// }
70
+ /// ```
35
71
///
36
- /// rustc can't infer types in closures when dealing with `Fn/FnMut/FnOnce` traits abstracted behind another trait (in our case
37
- /// [`SignalReceiver`]). To make type inference work in such cases, we need to specify concrete type – such as `FnMut( ...) -> R `
38
- /// or `< &mut F as Into<IndirectSignalReceiver<'_, Instance, Params, Func>>>` .
72
+ /// This second bound is necessary because rustc cannot infer parameter types in closures, when dealing with `Fn/FnMut/FnOnce` traits abstracted
73
+ /// behind another trait (in our case [`SignalReceiver`]). Without inference, it wouldn't be reliably possible to pass `|this, arg| { ... } `
74
+ /// closures and would require the more verbose `|this: &mut MyClass, arg: GString| { ... }` syntax .
39
75
///
40
- /// In other words, `IndirectSignalReceiver` allows us to "smuggle" in a `Fn*` trait, forcing rustc to deal with type inference while resolving
41
- /// its inner type.
76
+ /// To make type inference work in such cases, we need to specify concrete type, such as `FnMut(...) -> R` or
77
+ /// `<&mut F as Into<IndirectSignalReceiver<'_, MyClass, (Param0, Param1), Func>>>`. The ~~dirty hack~~ clever trick used here is to "smuggle" a
78
+ /// concrete `Fn*` trait through `IndirectSignalReceiver`. This forces rustc to resolve the concrete `Fn*` type.
42
79
///
43
- /// # Example usage
80
+ /// Prior designs included declarative macros to generate all `connect*` functions with direct `Fn*` bounds (not through `SignalReceiver`).
81
+ /// This works well, too, but prevents users from extending the functionality through generic programming -- they'd need to use macros, too.
82
+ ///
83
+ /// # Usage within `connect*` style methods
84
+ /// When using the trait bounds as described above, you can access the actual function in the following way:
44
85
///
45
86
/// ```no_run
46
87
/// # use godot::register::{IndirectSignalReceiver, SignalReceiver};
@@ -49,41 +90,55 @@ pub trait SignalReceiver<C, Ps>: 'static {
49
90
/// IndirectSignalReceiver::from(&mut function)
50
91
/// .function()
51
92
/// .call((), args);
52
- ///```
93
+ /// ```
94
+ ///
95
+ /// # Hidden `impls` in this doc
96
+ /// To keep this documentation readable, we only document one variant of each `impl From<F>`: arbitrarily the one with three parameters
97
+ /// `(P0, P1, P2)`. Keep this in mind when looking at a concrete signature.
53
98
///
54
99
/// # Further reading
55
100
/// - [rustc issue #63702](https://github.com/rust-lang/rust/issues/63702)
56
101
/// - [identity function trick](https://users.rust-lang.org/t/type-inference-in-closures/78399/3)
102
+ /// - [discussion about type-inference limits](https://users.rust-lang.org/t/what-are-the-limits-of-type-inference-in-closures/31519)
57
103
/// - [rustc comments around closure type-check](https://github.com/rust-lang/rust/blob/5ad7454f7503b6af2800bf4a7c875962cb03f913/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs#L306-L317)
58
- pub struct IndirectSignalReceiver < ' view , I , Ps , F >
104
+ pub struct IndirectSignalReceiver < ' view , C , Ps , F >
59
105
where
60
106
Ps : InParamTuple ,
61
- F : SignalReceiver < I , Ps > + ' static ,
107
+ F : SignalReceiver < C , Ps > + ' static ,
62
108
{
63
109
inner : & ' view mut F ,
64
- _phantoms : PhantomData < ( I , Ps ) > ,
110
+ _phantoms : PhantomData < ( C , Ps ) > ,
65
111
}
66
112
67
- impl < ' view , I , Ps , F > IndirectSignalReceiver < ' view , I , Ps , F >
113
+ impl < ' view , C , Ps , F > IndirectSignalReceiver < ' view , C , Ps , F >
68
114
where
69
115
Ps : InParamTuple ,
70
- F : SignalReceiver < I , Ps > + ' static ,
116
+ F : SignalReceiver < C , Ps > + ' static ,
71
117
{
72
118
/// Retrieves inner `&mut F` function ready to be used as [`SignalReceiver`].
73
119
pub fn function ( & ' view mut self ) -> & ' view mut F {
74
120
self . inner
75
121
}
122
+
123
+ /// Creates a new `IndirectSignalReceiver` from a mutable reference to a function.
124
+ fn new ( inner : & ' view mut F ) -> Self {
125
+ Self {
126
+ inner,
127
+ _phantoms : PhantomData ,
128
+ }
129
+ }
76
130
}
77
131
78
132
// ----------------------------------------------------------------------------------------------------------------------------------------------
79
133
// Generated impls
80
134
81
135
macro_rules! impl_signal_recipient {
82
- ( $( $args: ident : $Ps: ident ) ,* ) => {
136
+ ( $( # [ $attr : meta ] ) ? $ ( $args: ident : $Ps: ident ) ,* ) => {
83
137
// --------------------------------------------------------------------------------------------------------------------------------------
84
138
// SignalReceiver
85
139
86
- // Global and associated functions.
140
+ // SignalReceiver: Global and associated functions.
141
+ $( #[ $attr] ) ?
87
142
impl <F , R , $( $Ps: std:: fmt:: Debug + FromGodot + ' static ) ,* > SignalReceiver <( ) , ( $( $Ps, ) * ) > for F
88
143
where F : FnMut ( $( $Ps, ) * ) -> R + ' static
89
144
{
@@ -92,7 +147,8 @@ macro_rules! impl_signal_recipient {
92
147
}
93
148
}
94
149
95
- // Methods with mutable receiver - &mut self.
150
+ // SignalReceiver: Methods with mutable receiver - &mut self.
151
+ $( #[ $attr] ) ?
96
152
impl <F , R , C , $( $Ps: std:: fmt:: Debug + FromGodot + ' static ) ,* > SignalReceiver <& mut C , ( $( $Ps, ) * ) > for F
97
153
where F : FnMut ( & mut C , $( $Ps, ) * ) -> R + ' static
98
154
{
@@ -101,16 +157,20 @@ macro_rules! impl_signal_recipient {
101
157
}
102
158
}
103
159
104
- // Methods with immutable receiver - &self.
160
+ // Methods with immutable receiver - &self. Disabled until needed.
161
+ /*
162
+ $( #[$attr] )?
105
163
impl<F, R, C, $($Ps: std::fmt::Debug + FromGodot + 'static),*> SignalReceiver<&C, ( $($Ps,)* )> for F
106
164
where F: FnMut( &C, $($Ps,)* ) -> R + 'static
107
165
{
108
166
fn call(&mut self, instance: &C, ($($args,)*): ( $($Ps,)* )) {
109
167
self(instance, $($args,)*);
110
168
}
111
169
}
170
+ */
112
171
113
- // Methods with gd receiver - Gd<Self>.
172
+ // Methods with Gd receiver - Gd<Self>.
173
+ $( #[ $attr] ) ?
114
174
impl <F , R , C , $( $Ps: std:: fmt:: Debug + FromGodot + ' static ) ,* > SignalReceiver <Gd <C >, ( $( $Ps, ) * ) > for F
115
175
where F : FnMut ( Gd <C >, $( $Ps, ) * ) -> R + ' static , C : GodotClass
116
176
{
@@ -119,63 +179,65 @@ macro_rules! impl_signal_recipient {
119
179
}
120
180
}
121
181
122
- // --------------------------------------------------------------------------------------------------------------------------------------
182
+ // --------------------------------------------------------------------------------------------------------------------------------------
123
183
// FnMut -> IndirectSignalReceiver
124
184
125
- impl <' c_view, F , R , $( $Ps: std:: fmt:: Debug + FromGodot + ' static ) ,* > From <& ' c_view mut F > for IndirectSignalReceiver <' c_view, ( ) , ( $( $Ps, ) * ) , F >
185
+ // From: Global and associated functions.
186
+ $( #[ $attr] ) ?
187
+ impl <' c_view, F , R , $( $Ps: std:: fmt:: Debug + FromGodot + ' static ) ,* >
188
+ From <& ' c_view mut F > for IndirectSignalReceiver <' c_view, ( ) , ( $( $Ps, ) * ) , F >
126
189
where F : FnMut ( $( $Ps, ) * ) -> R + ' static
127
190
{
128
191
fn from( value: & ' c_view mut F ) -> Self {
129
- IndirectSignalReceiver {
130
- inner: value,
131
- _phantoms: PhantomData ,
132
- }
192
+ IndirectSignalReceiver :: new( value)
133
193
}
134
194
}
135
195
136
- impl <' c_view, F , R , C , $( $Ps: std:: fmt:: Debug + FromGodot + ' static ) ,* > From <& ' c_view mut F > for IndirectSignalReceiver <' c_view, & mut C , ( $( $Ps, ) * ) , F >
196
+ // From: Methods with mutable receiver - &mut self.
197
+ $( #[ $attr] ) ?
198
+ impl <' c_view, F , R , C , $( $Ps: std:: fmt:: Debug + FromGodot + ' static ) ,* >
199
+ From <& ' c_view mut F > for IndirectSignalReceiver <' c_view, & mut C , ( $( $Ps, ) * ) , F >
137
200
where F : FnMut ( & mut C , $( $Ps, ) * ) -> R + ' static
138
201
{
139
202
fn from( value: & ' c_view mut F ) -> Self {
140
- IndirectSignalReceiver {
141
- inner: value,
142
- _phantoms: PhantomData ,
143
- }
203
+ IndirectSignalReceiver :: new( value)
144
204
}
145
205
}
146
206
147
- impl <' c_view, F , R , C , $( $Ps: std:: fmt:: Debug + FromGodot + ' static ) ,* > From <& ' c_view mut F > for IndirectSignalReceiver <' c_view, & C , ( $( $Ps, ) * ) , F >
207
+ // From: Methods with immutable receiver - &self. Disabled until needed.
208
+ /*
209
+ $( #[$attr] )?
210
+ impl<'c_view, F, R, C, $($Ps: std::fmt::Debug + FromGodot + 'static),*>
211
+ From<&'c_view mut F> for IndirectSignalReceiver<'c_view, &C, ($($Ps,)*), F>
148
212
where F: FnMut( &C, $($Ps,)* ) -> R + 'static
149
213
{
150
214
fn from(value: &'c_view mut F) -> Self {
151
- IndirectSignalReceiver {
152
- inner: value,
153
- _phantoms: PhantomData ,
154
- }
215
+ IndirectSignalReceiver::new(value)
155
216
}
156
217
}
218
+ */
157
219
158
- impl <' c_view, F , R , C , $( $Ps: std:: fmt:: Debug + FromGodot + ' static ) ,* > From <& ' c_view mut F > for IndirectSignalReceiver <' c_view, Gd <C >, ( $( $Ps, ) * ) , F >
220
+ // From: Methods with Gd receiver - Gd<Self>.
221
+ $( #[ $attr] ) ?
222
+ impl <' c_view, F , R , C , $( $Ps: std:: fmt:: Debug + FromGodot + ' static ) ,* >
223
+ From <& ' c_view mut F > for IndirectSignalReceiver <' c_view, Gd <C >, ( $( $Ps, ) * ) , F >
159
224
where F : FnMut ( Gd <C >, $( $Ps, ) * ) -> R + ' static , C : GodotClass
160
225
{
161
226
fn from( value: & ' c_view mut F ) -> Self {
162
- IndirectSignalReceiver {
163
- inner: value,
164
- _phantoms: PhantomData ,
165
- }
227
+ IndirectSignalReceiver :: new( value)
166
228
}
167
229
}
168
230
} ;
169
231
}
170
232
171
- impl_signal_recipient ! ( ) ;
172
- impl_signal_recipient ! ( arg0: P0 ) ;
173
- impl_signal_recipient ! ( arg0: P0 , arg1: P1 ) ;
174
- impl_signal_recipient ! ( arg0: P0 , arg1: P1 , arg2: P2 ) ;
175
- impl_signal_recipient ! ( arg0: P0 , arg1: P1 , arg2: P2 , arg3: P3 ) ;
176
- impl_signal_recipient ! ( arg0: P0 , arg1: P1 , arg2: P2 , arg3: P3 , arg4: P4 ) ;
177
- impl_signal_recipient ! ( arg0: P0 , arg1: P1 , arg2: P2 , arg3: P3 , arg4: P4 , arg5: P5 ) ;
178
- impl_signal_recipient ! ( arg0: P0 , arg1: P1 , arg2: P2 , arg3: P3 , arg4: P4 , arg5: P5 , arg6: P6 ) ;
179
- impl_signal_recipient ! ( arg0: P0 , arg1: P1 , arg2: P2 , arg3: P3 , arg4: P4 , arg5: P5 , arg6: P6 , arg7: P7 ) ;
180
- impl_signal_recipient ! ( arg0: P0 , arg1: P1 , arg2: P2 , arg3: P3 , arg4: P4 , arg5: P5 , arg6: P6 , arg7: P7 , arg8: P8 ) ;
181
- impl_signal_recipient ! ( arg0: P0 , arg1: P1 , arg2: P2 , arg3: P3 , arg4: P4 , arg5: P5 , arg6: P6 , arg7: P7 , arg8: P8 , arg9: P9 ) ;
233
+ impl_signal_recipient ! ( # [ doc ( hidden ) ] ) ;
234
+ impl_signal_recipient ! ( # [ doc ( hidden ) ] arg0: P0 ) ;
235
+ impl_signal_recipient ! ( # [ doc ( hidden ) ] arg0: P0 , arg1: P1 ) ;
236
+ impl_signal_recipient ! ( arg0: P0 , arg1: P1 , arg2: P2 ) ;
237
+ impl_signal_recipient ! ( # [ doc ( hidden ) ] arg0: P0 , arg1: P1 , arg2: P2 , arg3: P3 ) ;
238
+ impl_signal_recipient ! ( # [ doc ( hidden ) ] arg0: P0 , arg1: P1 , arg2: P2 , arg3: P3 , arg4: P4 ) ;
239
+ impl_signal_recipient ! ( # [ doc ( hidden ) ] arg0: P0 , arg1: P1 , arg2: P2 , arg3: P3 , arg4: P4 , arg5: P5 ) ;
240
+ impl_signal_recipient ! ( # [ doc ( hidden ) ] arg0: P0 , arg1: P1 , arg2: P2 , arg3: P3 , arg4: P4 , arg5: P5 , arg6: P6 ) ;
241
+ impl_signal_recipient ! ( # [ doc ( hidden ) ] arg0: P0 , arg1: P1 , arg2: P2 , arg3: P3 , arg4: P4 , arg5: P5 , arg6: P6 , arg7: P7 ) ;
242
+ impl_signal_recipient ! ( # [ doc ( hidden ) ] arg0: P0 , arg1: P1 , arg2: P2 , arg3: P3 , arg4: P4 , arg5: P5 , arg6: P6 , arg7: P7 , arg8: P8 ) ;
243
+ impl_signal_recipient ! ( # [ doc ( hidden ) ] arg0: P0 , arg1: P1 , arg2: P2 , arg3: P3 , arg4: P4 , arg5: P5 , arg6: P6 , arg7: P7 , arg8: P8 , arg9: P9 ) ;
0 commit comments