Crate godot

source ·
Expand description

The gdext library implements Rust bindings for GDExtension, the C API of Godot 4.

This documentation is a work in progress.

§Type categories

Godot is written in C++, which doesn’t have the same strict guarantees about safety and mutability that Rust does. As a result, not everything in this crate will look and feel entirely “rusty”. See also Philosophy.

Traits such as Clone, PartialEq or PartialOrd are designed to mirror Godot semantics, except in cases where Rust is stricter (e.g. float ordering). Cloning a type results in the same observable behavior as assignment or parameter-passing of a GDScript variable.

We distinguish four different kinds of types:

  1. Value types: i64, f64, and mathematical types like Vector2 and Color.

    These are the simplest to understand and to work with. They implement Clone and often Copy as well. They are implemented with the same memory layout as their counterparts in Godot itself, and typically have public fields.

  2. Copy-on-write types: GString, StringName, and Packed*Array types.

    These mostly act like value types, similar to Rust’s own Vec. You can Clone them to get a full copy of the entire object, as you would expect.

    Under the hood in Godot, these types are implemented with copy-on-write, so that data can be shared until one of the copies needs to be modified. However, this performance optimization is entirely hidden from the API and you don’t normally need to worry about it.

  3. Reference-counted types: Array, Dictionary, and Gd<T> where T inherits from RefCounted.

    These types may share their underlying data between multiple instances: changes to one instance are visible in another. They are conceptually similar to Rc<RefCell<...>>.

    Since there is no way to prevent or even detect this sharing from Rust, you need to be more careful when using such types. For example, when iterating over an Array, make sure that it isn’t being modified at the same time through another reference.

    Clone::clone() on these types creates a new reference to the same instance, while type-specific methods such as Array::duplicate_deep() can be used to make actual copies.

  4. Manually managed types: Gd<T> where T inherits from Object but not from RefCounted; most notably, this includes all Node classes.

    These also share data, but do not use reference counting to manage their memory. Instead, you must either hand over ownership to Godot (e.g. by adding a node to the scene tree) or free them manually using Gd::free().

§Ergonomics and panics

gdext is designed with usage ergonomics in mind, making it viable for fast prototyping. Part of this design means that users should not constantly be forced to write code such as obj.cast::<T>().unwrap(). Instead, they can just write obj.cast::<T>(), which may panic at runtime.

This approach has several advantages:

  • The code is more concise and less cluttered.
  • Methods like cast() provide very sophisticated panic messages when they fail (e.g. involved classes), immediately giving you the necessary context for debugging. This is certainly preferable over a generic unwrap(), and in most cases also over a expect("literal").
  • Usually, such methods panicking indicate bugs in the application. For example, you have a static scene tree, and you know that a node of certain type and name exists. get_node_as::<T>("name") thus must succeed, or your mental concept is wrong. In other words, there is not much you can do at runtime to recover from such errors anyway; the code needs to be fixed.

Now, there are of course cases where you do want to check certain assumptions dynamically. Imagine a scene tree that is constructed at runtime, e.g. in a game editor. This is why the library provides “overloads” for most of these methods that return Option or Result. Such methods have more verbose names and highlight the attempt, e.g. try_cast().

To help you identify panicking methods, we use the symbol “⚠️” at the beginning of the documentation; this should also appear immediately in the auto-completion of your IDE. Note that this warning sign is not used as a general panic indicator, but particularly for methods which have a Option/Result-based overload. If you want to know whether and how a method can panic, check if its documentation has a Panics section.

§Thread safety

Godot’s own thread safety rules apply. Types in this crate implement (or don’t implement) Send and Sync wherever appropriate, but the Rust compiler cannot check what happens to an object through C++ or GDScript.

As a rule of thumb, if you must use threading, prefer to use Rust threads over Godot threads.

The Cargo feature experimental-threads provides experimental support for multithreading. The underlying safety rules are still being worked out, as such you may encounter unsoundness and an unstable API.

§Cargo features

The following features can be enabled for this crate. All off them are off by default.

Avoid default-features = false unless you know exactly what you are doing; it will disable some required internal features.

  • double-precision

    Use f64 instead of f32 for the floating-point type real. Requires Godot to be compiled with the scons flag precision=double.

  • custom-godot

    Use a custom Godot build instead of the latest official release. This is useful when you like to use a version compiled yourself, with custom flags.

    If you simply want to use a different official release, use this pattern instead (here e.g. for version 4.0):

    # Trick Cargo into seeing a different URL; https://github.com/rust-lang/cargo/issues/5478
    [patch."https://github.com/godot-rust/godot4-prebuilt"]
    godot4-prebuilt = { git = "https://github.com//godot-rust/godot4-prebuilt", branch = "4.0"}
    

  • serde

    Implement the serde traits Serialize and Deserialize traits for certain built-in types. The serialized representation underlies no stability guarantees and may change at any time, even without a SemVer-breaking change.

  • lazy-function-tables

    Instead of loading all engine function pointers at startup, load them lazily on first use. This reduces startup time and RAM usage, but incurs additional overhead in each FFI call. Also, you lose the guarantee that once the library has booted, all function pointers are truly available. Function calls may thus panic only at runtime, possibly in deeply nested code paths. This feature is not yet thread-safe and can thus not be combined with experimental-threads.

  • formatted

    Format the generated binding code with a custom-built formatter, which aims to strike a balance between runtime and human readability. rustfmt generates nice output, but it is unfortunately excessively slow across hundreds of Godot classes.

  • experimental-threads

    Experimental threading support. This enables Send/Sync traits for Gd<T> and makes the guard types Gd/GdMut aware of multi-threaded references. There safety aspects are not ironed out yet; there is a high risk of unsoundness at the moment. As this evolves, it is very likely that the API becomes more strict.

  • experimental-godot-api

    Access to godot::engine APIs that Godot marks “experimental”. These are under heavy development and may change at any time. If you opt in to this feature, expect breaking changes at compile and runtime.

  • experimental-wasm

    Support for WebAssembly exports is still a work-in-progress and is not yet well tested. This feature is in place for users to explicitly opt-in to any instabilities or rough edges that may result. Due to a limitation in Godot, it might currently not work Firefox browser.

§Public API

Some symbols in the API are not intended for users, however Rust’s visibility feature is not strong enough to express that in all cases (for example, proc-macros and separated crates may need access to internals).

The following API symbols are considered private:

  • Symbols annotated with #[doc(hidden)].
  • Any of the dependency crates (crate godot is the only public interface).
  • Modules named private and all their contents.

Being private means a workflow is not supported. As such, there are no guarantees regarding API stability, robustness or correctness. Problems arising from using such APIs are not considered bugs, and anything relying on them may stop working without announcement. Please refrain from using undocumented and private features; if you are missing certain functionality, bring it up for discussion instead. This allows us to decide whether it fits the scope of the library and to design proper APIs for it.

Modules§

  • Built-in types like Vector2, GString and Variant.
  • Maps the Godot class API to Rust.
  • Entry point and global init/shutdown of the library.
  • Printing and logging functionality.
  • Types and traits related to objects.
  • Often-imported symbols.
  • Register/export Rust symbols to Godot: classes, methods, enums…