Derive Macro godot::register::GodotClass

#[derive(GodotClass)]
{
    // Attributes available to this derive:
    #[class]
    #[base]
    #[hint]
    #[var]
    #[export]
    #[init]
    #[signal]
}
Expand description

Derive macro for GodotClass on structs.

You should use this macro; manual implementations of the GodotClass trait are not encouraged.

This is typically used in combination with #[godot_api], which can implement custom functions and constants, as well as override virtual methods.

See also book chapter Registering classes.

Table of contents:

§Construction

If you don’t override init() manually (within a #[godot_api] block), gdext can generate a default constructor for you. This constructor is made available to Godot and lets you call MyStruct.new() from GDScript. To enable it, annotate your struct with #[class(init)]:

#[derive(GodotClass)]
#[class(init)]
struct MyStruct {
    // ...
}

The generated init function will initialize each struct field (except the field of type Base<T>, if any) using Default::default(). To assign some other value, annotate the field with #[init(default = ...)]:

#[derive(GodotClass)]
#[class(init)]
struct MyStruct {
    #[init(default = 42)]
    my_field: i64
}

The given value can be any Rust expression that can be evaluated in the scope where you write the attribute. However, due to limitations in the parser, some complex expressions must be surrounded by parentheses. This is the case if the expression includes a , that is not inside any pair of (...), [...] or {...} (even if it is, for example, inside <...> or |...|). A contrived example:

#[init(default = (HashMap::<i64, i64>::new()))]
//                             ^ parentheses needed due to this comma
my_field: HashMap<i64, i64>,

You can also disable construction from GDScript. This needs to be explicit via #[class(no_init)]. Simply omitting the init/no_init keys and not overriding your own constructor will cause a compile error.

#[derive(GodotClass)]
#[class(no_init)]
struct MyStruct {
   // ...
}

§Inheritance

Unlike C++, Rust doesn’t really have inheritance, but the GDExtension API lets us “inherit” from a Godot-provided engine class.

By default, classes created with this library inherit from RefCounted, like GDScript.

To specify a different class to inherit from, add #[class(base = Base)] as an annotation on your struct:

#[derive(GodotClass)]
#[class(init, base=Node2D)]
struct MyStruct {
    // ...
}

If you need a reference to the base class, you can add a field of type Base<T>. The derive macro will pick this up and wire your object accordingly. You can access it through self.base() and self.base_mut() methods.

#[derive(GodotClass)]
#[class(init, base=Node2D)]
struct MyStruct {
    base: Base<Node2D>,
}

§Properties and exports

See also book chapter Registering properties.

In GDScript, there is a distinction between properties (fields with a get or set declaration) and exports (fields annotated with @export). In the gdext API, these two concepts are represented with #[var] and #[export] attributes respectively.

§Property registration

To create a property, you can use the #[var] annotation:

#[derive(GodotClass)]
struct MyStruct {
    #[var]
    my_field: i64,
}

This makes the field accessible in GDScript using my_struct.my_field syntax. Additionally, it generates a trivial getter and setter named get_my_field and set_my_field, respectively. These are pub in Rust, since they’re exposed from GDScript anyway.

If you want to implement your own getter and/or setter, write those as a function on your Rust type, expose it using #[func], and annotate the field with #[var(get = ..., set = ...)]:

#[derive(GodotClass)]
struct MyStruct {
    #[var(get = get_my_field, set = set_my_field)]
    my_field: i64,
}

#[godot_api]
impl MyStruct {
    #[func]
    pub fn get_my_field(&self) -> i64 {
        self.my_field
    }

    #[func]
    pub fn set_my_field(&mut self, value: i64) {
        self.my_field = value;
    }
}

If you specify only get, no setter is generated, making the field read-only. If you specify only set, no getter is generated, making the field write-only (rarely useful). To add a generated getter or setter in these cases anyway, use get or set without a value:

#[derive(GodotClass)]
struct MyStruct {
    // Default getter, custom setter.
    #[var(get, set = set_my_field)]
    my_field: i64,
}

#[godot_api]
impl MyStruct {
    #[func]
    pub fn set_my_field(&mut self, value: i64) {
        self.my_field = value;
    }
}

§Property exports

For exporting properties to the editor, you can use the #[export] attribute:

#[derive(GodotClass)]
struct MyStruct {
    #[export]
    my_field: i64,
}

If you don’t also include a #[var] attribute, then a default one will be generated. #[export] also supports all of GDScript’s annotations, in a slightly different format. The format is translated from an annotation by following these four rules:

  • @export becomes #[export]
  • @export_{name} becomes #[export(name)]
  • @export_{name}(elem1, ...) becomes #[export(name = (elem1, ...))]
  • @export_{flags/enum}("elem1", "elem2:key2", ...) becomes #[export(flags/enum = (elem1, elem2 = key2, ...))]

As an example of some different export attributes:

#[derive(GodotClass)]
struct MyStruct {
    // @export
    #[export]
    float: f64,
     
    // @export_range(0.0, 10.0, or_greater)
    #[export(range = (0.0, 10.0, or_greater))]
    range_f64: f64,

    // @export_file
    #[export(file)]
    file: GString,

    // @export_file("*.gd")
    #[export(file = "*.gd")]
    gdscript_file: GString,

    // @export_flags_3d_physics
    #[export(flags_3d_physics)]
    physics: u32,

    // @export_exp_easing
    #[export(exp_easing)]
    ease: f64,

    // @export_enum("One", "Two", "Ten:10", "Twelve:12", "Thirteen")
    #[export(enum = (One, Two, Ten = 10, Twelve = 12, Thirteen))]
    exported_enum: i64,

    // @export_flags("A:1", "B:2", "AB:3")
    #[export(flags = (A = 1, B = 2, AB = 3))]
    flags: u32,
}

Most values in expressions like key = value, can be an arbitrary expression that evaluates to the right value. Meaning you can use constants or variables, as well as any other rust syntax you’d like in the export attributes.

const MAX_HEALTH: f64 = 100.0;

#[derive(GodotClass)]
struct MyStruct {
    #[export(range = (0.0, MAX_HEALTH))]
    health: f64,

    #[export(flags = (A = 0b0001, B = 0b0010, C = 0b0100, D = 0b1000))]
    flags: u32,
}

You can specify custom property hints, hint strings, and usage flags in a #[var] attribute using the hint, hint_string, and usage_flags keys in the attribute. These are constants in the PropertyHint and PropertyUsageFlags enums, respectively.

#[derive(GodotClass)]
struct MyStruct {
    // Treated as an enum with two values: "One" and "Two"
    // Displayed in the editor
    // Treated as read-only by the editor
    #[var(
        hint = ENUM,
        hint_string = "One,Two",
        usage_flags = [EDITOR, READ_ONLY]
    )]
    my_field: i64,
}

§Signals

The #[signal] attribute is quite limited at the moment. The functions it decorates (the signals) can accept parameters. It will be fundamentally reworked.

#[derive(GodotClass)]
struct MyClass {}

#[godot_api]
impl MyClass {
    #[signal]
    fn some_signal();

    #[signal]
    fn some_signal_with_parameters(my_parameter: Gd<Node>);
}

§Further class customization

§Running code in the editor

If you annotate a class with #[class(tool)], its lifecycle methods (ready(), process() etc.) will be invoked in the editor. This is useful for writing custom editor plugins, as opposed to classes running simply in-game.

See ExtensionLibrary::editor_run_behavior() for more information and further customization.

This is very similar to GDScript’s @tool feature.

§Editor plugins

If you annotate a class with #[class(editor_plugin)], it will be turned into an editor plugin. The class must then inherit from EditorPlugin, and an instance of that class will be automatically added to the editor when launched.

See Godot’s documentation of editor plugins for more information about editor plugins. But note that you do not need to create and enable the plugin through Godot’s Create New Plugin menu for it to work, simply annotating the class with editor_plugin automatically enables it when the library is loaded.

This should usually be combined with #[class(tool)] so that the code you write will actually run in the editor.

§Class renaming

You may want to have structs with the same name. With Rust, this is allowed using mod. However in GDScript, there are no modules, namespaces, or any such disambiguation. Therefore, you need to change the names before they can get to Godot. You can use the rename key while defining your GodotClass for this.

mod animal {
    #[derive(GodotClass)]
    #[class(init, rename=AnimalToad)]
    pub struct Toad {}
}

mod npc {
    #[derive(GodotClass)]
    #[class(init, rename=NpcToad)]
    pub struct Toad {}
}

These classes will appear in the Godot editor and GDScript as “AnimalToad” or “NpcToad”.

§Class hiding

If you want to register a class with Godot, but not have it show up in the editor then you can use #[class(hidden)].

#[derive(GodotClass)]
#[class(base=Node, init, hidden)]
pub struct Foo {}

Even though this class is a Node and it has an init function, it still won’t show up in the editor as a node you can add to a scene because we have added a hidden key to the class. This will also prevent it from showing up in documentation.

§Further field customization

§Fine-grained inference hints

The derive macro is relatively smart about recognizing Base<T> and OnReady<T> types, and works also if those are qualified.

However, there may be situations where you need to help it out – for example, if you have a type alias for Base<T>, or use an unrelated my_module::Base<T> with a different meaning.

In this case, you can manually override the behavior with the #[hint] attribute. It takes multiple standalone keys:

  • base and no_base
  • onready and no_onready
use godot::engine::Node;

// There's no reason to do this, but for the sake of example:
type Super<T> = godot::obj::Base<T>;
type Base<T> = godot::obj::Gd<T>;

#[derive(godot::register::GodotClass)]
#[class(base=Node)]
struct MyStruct {
   #[hint(base)]
   base: Super<Node>,

   #[hint(no_base)]
   unbase: Base<Node>,
}