Registering functions

Functions are essential in any programming language to execute logic. The gdext library allows you to register functions, so that they can be called from the Godot engine and GDScript.

Registration of functions happens always inside impl blocks that are annotated with #[godot_api].

See also GDScript reference for functions.

Table of contents

Godot special functions

Interface traits

Each engine class comes with an associated trait, which has the same name but is prefixed with the letter I, for "Interface". The trait has no required functions, but you can override any functions to customize the behavior towards Godot.

Any impl block for the trait must be annotated with the #[godot_api] attribute macro.

godot_api macro

The attribute proc-macro #[godot_api] is applied to impl blocks and marks their items for registration. It takes no arguments.

See API docs for detailed information.

Functions provided by the interface trait (beginning with I) are called Godot special functions. These can be overridden and allow you to influence the behavior of an object. Most common is a hook into the lifecycle of your object, defining some logic that is run upon certain events like creation, scene-tree entering, or per-frame updates.

In our case, the Node3D comes with the INode3D trait. Here is a small selection of its lifecycle methods. For a complete list, see INode3D docs.

#![allow(unused)]
fn main() {
#[godot_api]
impl INode3D for Monster {
    // Instantiate the object.
    fn init(base: Base<Node3D>) -> Self { ... }
    
    // Called when the node is ready in the scene tree.
    fn ready(&mut self) { ... }
    
    // Called every frame.
    fn process(&mut self, delta: f64) { ... }
    
    // Called every physics frame.
    fn physics_process(&mut self, delta: f64) { ... }
    
    // String representation of the object.
    fn to_string(&self) -> GString { ... }
    
    // Handle user input.
    fn input(&mut self, event: Gd<InputEvent>) { ... }
    
    // Handle lifecycle notifications.
    fn on_notification(&mut self, what: Node3DNotification) { ... }
}
}

As you see, some methods take &mut self and some take &self, depending on whether they typically mutate the object or not. Some also have return values, which are passed back into the engine. For example, the GString returned from to_string() is used if you print an object in GDScript.

So let's implement to_string(), here again showing the class definition for quick reference.

#![allow(unused)]
fn main() {
#[derive(GodotClass)]
#[class(base=Node3D)]
struct Monster {
    name: String,
    hitpoints: i32
    
    base: Base<Node3D>,
}

#[godot_api]
impl INode3D for Monster {      
    fn to_string(&self) -> GString {
        let Self { name, hitpoints, .. } = &self;
        format!("Monster(name={name}, hp={hitpoints})").into()
    }
}
}

User-defined functions

Methods

Besides Godot special functions, you can register your own functions. You need to declare them inside an inherent impl block, also annotated with #[godot_api].

Each function needs a #[func] attribute to register it with Godot. You can omit #[func] as well, but functions defined like that are only visible to Rust code.

Let's add two methods to our Monster class: one that deals damage to the monster, and one that returns its name.

#![allow(unused)]
fn main() {
#[godot_api]
impl Monster {
    #[func]
    fn damage(&mut self, amount: i32) {
        self.hitpoints -= amount;
    }
    
    #[func]
    fn get_name(&self) -> GString {
        self.name.clone()
    }
}
}

The above methods are now available in GDScript. You can call them as follows:

var monster = Monster.new()
# ...
monster.damage(10)
print("A monster called ", monster.get_name())

As you see, the Rust types are automatically mapped to their GDScript counterparts. In this case, i32 becomes int and GString becomes String. Sometimes there are multiple possible mappings, e.g. Rust u16 would also be mapped to int in GDScript.

Associated functions

In addition to methods (taking &self or &mut self), you can also register associated functions (without a receiver). In GDScript, the latter are known as "static functions".

For example, we can add an associated function which generates a random monster name:

#![allow(unused)]
fn main() {
#[godot_api]
impl Monster {
    #[func]
    fn random_name() -> GString {
        // ...
    }
}
}

The above can then be called from GDScript as follows:

var name: String = Monster.random_name()

Of course, it is also possible to declare parameters.

Associated functions are sometimes useful for user-defined constructors, as we will see in the next chapter.

Conclusion

This page gave you an overview of registering functions with Godot:

  • Special methods that hook into the lifecycle of your object.
  • User-defined methods and associated functions to expose a Rust API to Godot.

These are just a few use cases, you are very flexible in how you design your interface between Rust and GDScript. In the next page, we will look into a special kind of functions: constructors.