Skip to content

Commit 8eb9875

Browse files
committed
Various doc improvements, mostly around signal + async
1 parent d18785d commit 8eb9875

File tree

12 files changed

+190
-87
lines changed

12 files changed

+190
-87
lines changed

godot-core/src/builtin/collections/packed_array.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ macro_rules! impl_packed_array {
5858
$($trait_impls:tt)*
5959
},
6060
) => {
61-
#[doc = concat!("Implements Godot's `", stringify!($PackedArray), "` type,")]
62-
#[doc = concat!("which is a space-efficient array of `", stringify!($Element), "`s.")]
61+
#[doc = concat!("Space-efficient array of [`", stringify!($Element), "`] elements.")]
6362
///
6463
/// Check out the [book](https://godot-rust.github.io/book/godot-api/builtins.html#packed-arrays) for a tutorial on packed arrays.
6564
///

godot-core/src/builtin/real.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ macro_rules! real {
196196
}};
197197
}
198198

199-
/// Array of reals.
199+
/// Array of [`real`]s.
200200
///
201201
/// The expression has type `[real; N]` where `N` is the number of elements in the array.
202202
///

godot-core/src/builtin/signal.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@ use crate::obj::bounds::DynMemory;
1919
use crate::obj::{Bounds, Gd, GodotClass, InstanceId};
2020
use sys::{ffi_methods, GodotFfi};
2121

22-
/// A `Signal` represents a signal of an Object instance in Godot.
22+
/// Untyped Godot signal.
2323
///
24-
/// Signals are composed of a reference to an `Object` and the name of the signal on this object.
24+
/// Signals are composed of a pointer to an `Object` and the name of the signal on this object.
2525
///
26-
/// # Godot docs
26+
/// In Rust, you might want to work with type-safe signals, available under the [`TypedSignal`](crate::registry::signal::TypedSignal) struct.
2727
///
28+
/// # Godot docs
2829
/// [`Signal` (stable)](https://docs.godotengine.org/en/stable/classes/class_signal.html)
2930
pub struct Signal {
3031
opaque: sys::types::OpaqueSignal,
@@ -75,7 +76,7 @@ impl Signal {
7576
/// A signal can only be connected once to the same [`Callable`]. If the signal is already connected,
7677
/// returns [`Error::ERR_INVALID_PARAMETER`] and
7778
/// pushes an error message, unless the signal is connected with [`ConnectFlags::REFERENCE_COUNTED`](crate::classes::object::ConnectFlags::REFERENCE_COUNTED).
78-
/// To prevent this, use [`Self::is_connected`] first to check for existing connections.
79+
/// To prevent this, check for existing connections with [`is_connected()`][Self::is_connected].
7980
pub fn connect(&self, callable: &Callable, flags: i64) -> Error {
8081
let error = self.as_inner().connect(callable, flags);
8182

@@ -84,7 +85,7 @@ impl Signal {
8485

8586
/// Disconnects this signal from the specified [`Callable`].
8687
///
87-
/// If the connection does not exist, generates an error. Use [`Self::is_connected`] to make sure that the connection exists.
88+
/// If the connection does not exist, generates an error. Use [`is_connected()`](Self::is_connected) to make sure that the connection exists.
8889
pub fn disconnect(&self, callable: &Callable) {
8990
self.as_inner().disconnect(callable);
9091
}

godot-core/src/builtin/string/gstring.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ impl GString {
8484
///
8585
/// Some notes on the encodings:
8686
/// - **Latin-1:** Since every byte is a valid Latin-1 character, no validation besides the `NUL` byte is performed.
87-
/// It is your responsibility to ensure that the input is valid Latin-1.
87+
/// It is your responsibility to ensure that the input is meaningful under Latin-1.
8888
/// - **ASCII**: Subset of Latin-1, which is additionally validated to be valid, non-`NUL` ASCII characters.
8989
/// - **UTF-8**: The input is validated to be UTF-8.
9090
///

godot-core/src/builtin/string/string_name.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ impl StringName {
6767
///
6868
/// Some notes on the encodings:
6969
/// - **Latin-1:** Since every byte is a valid Latin-1 character, no validation besides the `NUL` byte is performed.
70-
/// It is your responsibility to ensure that the input is valid Latin-1.
70+
/// It is your responsibility to ensure that the input is meaningful under Latin-1.
7171
/// - **ASCII**: Subset of Latin-1, which is additionally validated to be valid, non-`NUL` ASCII characters.
7272
/// - **UTF-8**: The input is validated to be UTF-8.
7373
///

godot-core/src/obj/traits.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -499,13 +499,21 @@ pub trait WithUserSignals: WithSignals + WithBaseField {
499499
/// fn damage_taken(&mut self, amount: i32);
500500
/// ```
501501
/// ...then you can access the signal as `self.signals().damage_taken()`, which returns an object with the following API:
502+
/// ```ignore
503+
/// // Connects global or associated function, or a closure.
504+
/// fn connect(f: impl FnMut(i32));
505+
///
506+
/// // Connects a &mut self method or closure on the emitter object.
507+
/// fn connect_self(f: impl FnMut(&mut Self, i32));
502508
///
503-
/// | Method signature | Description |
504-
/// |------------------|-------------|
505-
/// | `connect(f: impl FnMut(i32))` | Connects global or associated function, or a closure. |
506-
/// | `connect_self(f: impl FnMut(&mut Self, i32))` | Connects a `&mut self` method or closure. |
507-
/// | `emit(amount: i32)` | Emits the signal with the given arguments. |
509+
/// // Connects a &mut self method or closure on another object.
510+
/// fn connect_other<C>(f: impl FnMut(&mut C, i32));
511+
///
512+
/// // Emits the signal with the given arguments.
513+
/// fn emit(amount: i32);
514+
/// ```
508515
///
516+
/// See [`TypedSignal`](crate::registry::signal::TypedSignal) for more information.
509517
fn signals(&mut self) -> Self::SignalCollection<'_, Self>;
510518
}
511519

godot-core/src/registry/signal/signal_receiver.rs

Lines changed: 112 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,64 @@ use std::marker::PhantomData;
2424
/// - `&mut C` for `&mut self` methods.
2525
///
2626
/// 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.
2738
pub trait SignalReceiver<C, Ps>: 'static {
2839
/// Invoke the receiver on the given instance (possibly `()`) with `params`.
2940
fn call(&mut self, maybe_instance: C, params: Ps);
3041
}
3142

3243
// Next-gen trait solver should allow for type inference in closures, when function traits are involved, without using identity struct
3344
// 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+
/// ```
3571
///
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.
3975
///
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.
4279
///
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:
4485
///
4586
/// ```no_run
4687
/// # use godot::register::{IndirectSignalReceiver, SignalReceiver};
@@ -49,41 +90,55 @@ pub trait SignalReceiver<C, Ps>: 'static {
4990
/// IndirectSignalReceiver::from(&mut function)
5091
/// .function()
5192
/// .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.
5398
///
5499
/// # Further reading
55100
/// - [rustc issue #63702](https://github.com/rust-lang/rust/issues/63702)
56101
/// - [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)
57103
/// - [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>
59105
where
60106
Ps: InParamTuple,
61-
F: SignalReceiver<I, Ps> + 'static,
107+
F: SignalReceiver<C, Ps> + 'static,
62108
{
63109
inner: &'view mut F,
64-
_phantoms: PhantomData<(I, Ps)>,
110+
_phantoms: PhantomData<(C, Ps)>,
65111
}
66112

67-
impl<'view, I, Ps, F> IndirectSignalReceiver<'view, I, Ps, F>
113+
impl<'view, C, Ps, F> IndirectSignalReceiver<'view, C, Ps, F>
68114
where
69115
Ps: InParamTuple,
70-
F: SignalReceiver<I, Ps> + 'static,
116+
F: SignalReceiver<C, Ps> + 'static,
71117
{
72118
/// Retrieves inner `&mut F` function ready to be used as [`SignalReceiver`].
73119
pub fn function(&'view mut self) -> &'view mut F {
74120
self.inner
75121
}
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+
}
76130
}
77131

78132
// ----------------------------------------------------------------------------------------------------------------------------------------------
79133
// Generated impls
80134

81135
macro_rules! impl_signal_recipient {
82-
($( $args:ident : $Ps:ident ),*) => {
136+
($( #[$attr:meta] )? $( $args:ident : $Ps:ident ),*) => {
83137
// --------------------------------------------------------------------------------------------------------------------------------------
84138
// SignalReceiver
85139

86-
// Global and associated functions.
140+
// SignalReceiver: Global and associated functions.
141+
$( #[$attr] )?
87142
impl<F, R, $($Ps: std::fmt::Debug + FromGodot + 'static),*> SignalReceiver<(), ( $($Ps,)* )> for F
88143
where F: FnMut( $($Ps,)* ) -> R + 'static
89144
{
@@ -92,7 +147,8 @@ macro_rules! impl_signal_recipient {
92147
}
93148
}
94149

95-
// Methods with mutable receiver - &mut self.
150+
// SignalReceiver: Methods with mutable receiver - &mut self.
151+
$( #[$attr] )?
96152
impl<F, R, C, $($Ps: std::fmt::Debug + FromGodot + 'static),*> SignalReceiver<&mut C, ( $($Ps,)* )> for F
97153
where F: FnMut( &mut C, $($Ps,)* ) -> R + 'static
98154
{
@@ -101,16 +157,20 @@ macro_rules! impl_signal_recipient {
101157
}
102158
}
103159

104-
// Methods with immutable receiver - &self.
160+
// Methods with immutable receiver - &self. Disabled until needed.
161+
/*
162+
$( #[$attr] )?
105163
impl<F, R, C, $($Ps: std::fmt::Debug + FromGodot + 'static),*> SignalReceiver<&C, ( $($Ps,)* )> for F
106164
where F: FnMut( &C, $($Ps,)* ) -> R + 'static
107165
{
108166
fn call(&mut self, instance: &C, ($($args,)*): ( $($Ps,)* )) {
109167
self(instance, $($args,)*);
110168
}
111169
}
170+
*/
112171

113-
// Methods with gd receiver - Gd<Self>.
172+
// Methods with Gd receiver - Gd<Self>.
173+
$( #[$attr] )?
114174
impl<F, R, C, $($Ps: std::fmt::Debug + FromGodot + 'static),*> SignalReceiver<Gd<C>, ( $($Ps,)* )> for F
115175
where F: FnMut( Gd<C>, $($Ps,)* ) -> R + 'static, C: GodotClass
116176
{
@@ -119,63 +179,65 @@ macro_rules! impl_signal_recipient {
119179
}
120180
}
121181

122-
// --------------------------------------------------------------------------------------------------------------------------------------
182+
// --------------------------------------------------------------------------------------------------------------------------------------
123183
// FnMut -> IndirectSignalReceiver
124184

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>
126189
where F: FnMut( $($Ps,)* ) -> R + 'static
127190
{
128191
fn from(value: &'c_view mut F) -> Self {
129-
IndirectSignalReceiver {
130-
inner: value,
131-
_phantoms: PhantomData,
132-
}
192+
IndirectSignalReceiver::new(value)
133193
}
134194
}
135195

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>
137200
where F: FnMut( &mut C, $($Ps,)* ) -> R + 'static
138201
{
139202
fn from(value: &'c_view mut F) -> Self {
140-
IndirectSignalReceiver {
141-
inner: value,
142-
_phantoms: PhantomData,
143-
}
203+
IndirectSignalReceiver::new(value)
144204
}
145205
}
146206

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>
148212
where F: FnMut( &C, $($Ps,)* ) -> R + 'static
149213
{
150214
fn from(value: &'c_view mut F) -> Self {
151-
IndirectSignalReceiver {
152-
inner: value,
153-
_phantoms: PhantomData,
154-
}
215+
IndirectSignalReceiver::new(value)
155216
}
156217
}
218+
*/
157219

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>
159224
where F: FnMut( Gd<C>, $($Ps,)* ) -> R + 'static, C: GodotClass
160225
{
161226
fn from(value: &'c_view mut F) -> Self {
162-
IndirectSignalReceiver {
163-
inner: value,
164-
_phantoms: PhantomData,
165-
}
227+
IndirectSignalReceiver::new(value)
166228
}
167229
}
168230
};
169231
}
170232

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

Comments
 (0)