@@ -80,7 +80,7 @@ So let's implement `to_string()`, here again showing the class definition for qu
80
80
#[class(base= Node3D )]
81
81
struct Monster {
82
82
name : String ,
83
- hitpoints : i32
83
+ hitpoints : i32 ,
84
84
85
85
base : Base <Node3D >,
86
86
}
@@ -163,8 +163,171 @@ Of course, it is also possible to declare parameters.
163
163
164
164
Associated functions are sometimes useful for user-defined constructors, as we will see in the next chapter.
165
165
166
- <!-- TODO: base() + base_mut() -->
167
- <!-- TODO: bind() + bind_mut() and their relation to &self/&mut self -->
166
+
167
+ ## Methods and object access
168
+
169
+ When you define your own Rust functions, there are two use cases that occur very frequently:
170
+
171
+ - You want to invoke your Rust methods from outside, through a ` Gd ` pointer.
172
+ - You want to access methods of the base class (e.g. ` Node3D ` ).
173
+
174
+ This section explains how to do both.
175
+
176
+
177
+ ### Calling Rust methods (binds)
178
+
179
+ If you now have a ` monster: Gd<Monster> ` , which stores a ` Monster ` object as defined above, you won't be able to simply call
180
+ ` monster.damage(123) ` . Rust is stricter than C++ and requires that only one ` &mut Monster ` reference exists at any point in time. Since
181
+ ` Gd ` pointers can be freely cloned, direct access through ` DerefMut ` wouldn't be sufficient to ensure non-aliasing.
182
+
183
+ To approach this, godot-rust uses the interior mutability pattern, which is quite similar to how [ ` RefCell ` ] [ rust-refcell ] works.
184
+
185
+ In short, whenever you need shared (immutable) access to a Rust object from a ` Gd ` pointer, use [ ` Gd::bind() ` ] [ api-gd-bind ] .
186
+ Whenever you need exclusive (mutable) access, use [ ` Gd::bind_mut() ` ] [ api-gd-bindmut ] .
187
+
188
+ ``` rust
189
+ let monster : Gd <Monster > = ... ;
190
+
191
+ // Immutable access with bind():
192
+ let name : GString = monster . bind (). get_name ();
193
+
194
+ // Mutable access with bind_mut() -- we rebind the object first:
195
+ let mut monster = monster ;
196
+ monster . bind_mut (). damage (123 );
197
+ ```
198
+
199
+ Regular Rust visibility rules apply: if your function should be visible in another module, declare it as ` pub ` or ` pub(crate) ` .
200
+
201
+ ``` admonish note title="The need for #[func]"
202
+ The `#[func]` attribute _only_ makes a function available to the Godot engine. It is orthogonal to Rust visibility (`pub`, `pub(crate)`, ...)
203
+ and does not influence whether a method can be accessed through `Gd::bind()` and `Gd::bind_mut()`.
204
+
205
+ If you only need to call a function in Rust, do not annotate it with `#[func]`. You can always add this later.
206
+ ```
207
+
208
+ ` bind() ` and ` bind_mut() ` return _ guard objects_ . At runtime, the library verifies that the borrow rules are upheld, and panics otherwise.
209
+ It can be beneficial to reuse guards across multiple statements, but make sure to keep their scope limited to not unnecessarily constrain access
210
+ to objects (especially when using ` bind_mut() ` ).
211
+
212
+ ``` rust
213
+ fn apply_monster_damage (mut monster : Gd <Monster >, raw_damage : i32 ) {
214
+ // Artificial scope:
215
+ {
216
+ let guard = monster . bind_mut (); // locks object -->
217
+ let armor = guard . get_armor_multiplier ();
218
+
219
+ let damage = (raw_damage as f32 * armor ) as i32 ;
220
+
221
+ guard . damage (damage )
222
+ } // <-- until here, where guard lifetime ends.
223
+
224
+ // Now you can pass the pointer on to other routines again.
225
+ check_if_dead (monster );
226
+ }
227
+ ```
228
+
229
+
230
+ ### Base access from ` self `
231
+
232
+ Within a class, you don't directly have a ` Gd<T> ` pointing to the own instance with base class methods. So you cannot use the approach explained
233
+ in the [ _ Calling functions_ chapter] [ book-godot-api-functions ] , where you would simply use ` gd.set_position(...) ` or similar.
234
+
235
+ Instead, you can access base class APIs via [ ` base() ` and ` base_mut() ` ] [ api-withbasefield-base ] . This requires that your class defines a
236
+ ` Base<T> ` field. Let's say we add a ` velocity ` field and two new methods:
237
+
238
+ ``` rust
239
+ #[derive(GodotClass )]
240
+ #[class(base= Node3D )]
241
+ struct Monster {
242
+ // ...
243
+ velocity : Vector2 ,
244
+ base : Base <Node3D >,
245
+ }
246
+
247
+ #[godot_api]
248
+ impl Monster {
249
+ pub fn apply_movement (& mut self , delta : f32 ) {
250
+ // Read access:
251
+ let pos = self . base (). get_position ();
252
+
253
+ // Write access (mutating methods):
254
+ self . base_mut (). set_position (pos + self . velocity * delta )
255
+ }
256
+
257
+ // This method has only read access (&self).
258
+ pub fn is_inside_area (& self , rect : Rect2 ) -> String
259
+ {
260
+ // We can only call base() here, not base_mut().
261
+ let node_name = self . base (). get_name ();
262
+
263
+ format! (" Monster(name={}, velocity={})" , node_name , self . velocity)
264
+ }
265
+ }
266
+ ```
267
+
268
+ Both ` base() ` and ` base_mut() ` are defined in an extension trait [ ` WithBaseField ` ] [ api-withbasefield ] . They return _ guard objects_ , which prevent
269
+ other access to ` self ` in line with Rust's borrow rules. You can reuse a guard across multiple statements, but make sure to keep its scope
270
+ limited to not unnecessarily constrain access to ` self ` :
271
+
272
+ ``` rust
273
+ pub fn apply_movement (& mut self , delta : f32 ) {
274
+ // Artificial scope:
275
+ {
276
+ let guard = self . base_mut (); // locks `self` -->
277
+ let pos = guard . get_position ();
278
+
279
+ guard . set_position (pos + self . velocity * delta )
280
+ } // <-- until here, where guard lifetime ends.
281
+
282
+ // Now can invoke other self methods again.
283
+ self . on_position_updated ();
284
+ }
285
+ ```
286
+
287
+
288
+ Instead of an extra scope, you can of course also just call [ ` drop(guard) ` ] [ rust-mem-drop ] .
289
+
290
+
291
+ ``` admonish note title="Do not combine bind/bind_mut + base/base_mut"
292
+ Code like `object.bind().base().some_method()` is unnecessarily verbose and slow.
293
+ If you have a `Gd<T>` pointer, use `object.some_method()` directly.
294
+
295
+ Combining `bind()`/`bind_mut()` immediately with `base()`/`base_mut()`
296
+ is a mistake. The latter two should only be called from within the class `impl`.
297
+ ```
298
+
299
+
300
+ ### Obtaining ` Gd<Self> ` from within
301
+
302
+ In some cases, you need to get a ` Gd<T> ` pointer to the current instance. This can occur if you want to pass it to other methods, or if you need
303
+ to store a pointer to ` self ` in a data structure.
304
+
305
+ ` WithBaseField ` offers a method ` to_gd() ` , returning a ` Gd<Self> ` with the correct type.
306
+
307
+ Here’s an example. The ` monster ` is passed a hash map, in which it can register/unregister itself, depending on whether it's alive or not.
308
+
309
+ ``` rust
310
+ #[godot_api]
311
+ impl Monster {
312
+ // Function that registers each monster by name, or unregisters it if dead.
313
+ fn update_registry (& self , registry : & mut HashMap <String , Gd <Monster >>) {
314
+ if self . is_alive () {
315
+ let self_as_gd : Gd <Self > = self . to_gd ();
316
+ registry . insert (self . name. clone (), self_as_gd );
317
+ } else {
318
+ registry . remove (& self . name);
319
+ }
320
+ }
321
+ }
322
+ ```
323
+
324
+ ``` admonish warning title="Don't bind to_gd() inside class methods"
325
+ The methods `base()` and `base_mut()` use a clever mechanism that "re-borrows" the current object reference. This enables re-entrant calls,
326
+ such as `self.base().notify(...)`, which may e.g. call `ready(&mut self)`. The `&mut self` here is a reborrow of the call-site `self`.
327
+
328
+ When you use `to_gd()`, the borrow checker will treat this as an independent object. If you call `bind_mut()` on it, while inside the class impl,
329
+ you will immediately get a double-borrow panic. Intead, use `to_gd()` to hand out a pointer and don't access until the current method has ended.
330
+ ```
168
331
169
332
170
333
## Conclusion
@@ -174,9 +337,18 @@ This page gave you an overview of registering functions with Godot:
174
337
- Special methods that hook into the lifecycle of your object.
175
338
- User-defined methods and associated functions to expose a Rust API to Godot.
176
339
340
+ It also showed how methods and objects interact: calling Rust methods through ` Gd<T> ` and working with base class APIs.
341
+
177
342
These are just a few use cases, you are very flexible in how you design your interface between Rust and GDScript.
178
343
In the next page, we will look into a special kind of functions: constructors.
179
344
180
345
[ api-godot-api ] : https://godot-rust.github.io/docs/gdext/master/godot/register/attr.godot_api.html
181
346
[ api-inode3d ] : https://godot-rust.github.io/docs/gdext/master/godot/classes/trait.INode3D.html
182
347
[ godot-gdscript-functions ] : https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#functions
348
+ [ api-withbasefield ] : https://godot-rust.github.io/docs/gdext/master/godot/obj/trait.WithBaseField.html
349
+ [ api-withbasefield-base ] : https://godot-rust.github.io/docs/gdext/master/godot/obj/trait.WithBaseField.html#method.base
350
+ [ rust-refcell ] : https://doc.rust-lang.org/std/cell/struct.RefCell.html
351
+ [ rust-mem-drop ] : https://doc.rust-lang.org/std/mem/fn.drop.html
352
+ [ book-godot-api-functions ] : ../godot-api/functions.html#godot-functions
353
+ [ api-gd-bind ] : https://godot-rust.github.io/docs/gdext/master/godot/prelude/struct.Gd.html#method.bind
354
+ [ api-gd-bindmut ] : https://godot-rust.github.io/docs/gdext/master/godot/prelude/struct.Gd.html#method.bind_mut
0 commit comments