A source-to-source transpiler that converts GDScript (.gd) files into Rust GDExtension code, compiling to a native shared library (.dll / .so / .dylib) that Godot 4 loads directly.
| Goal | Detail |
|---|---|
| IP protection | Compiled Rust is not trivially decompiled; GDScript ships as plain text or lightly-bytecoded PCK files |
| Performance | Hot paths (AI, physics helpers, procedural generation) benefit from Rust's zero-cost abstractions |
| Drop-in replacement | Generated code registers the same class names, method names, signals, and properties as the original GDScript |
| Incremental adoption | Transpile one file at a time; the rest of the project stays in GDScript |
- Rust (2021 edition)
- Godot 4.6.1 (bundled API; overridable via
--godot-api) rustfmt(optional, for--format)
cargo install --path .gd2rust <files>... [OPTIONS]
Arguments:
| Argument | Default | Description |
|---|---|---|
<files>... |
required | .gd files or directories to transpile (directories are scanned recursively) |
-o, --output <PATH> |
output |
Output directory for the generated Rust project |
-n, --name <NAME> |
my_game |
Name of the generated GDExtension project |
--godot-api <PATH> |
bundled | Replace the bundled Godot extension_api.json entirely (use when targeting a different engine version or a custom build) |
--extension-api <PATH> |
— | Add a third-party plugin API on top of the built-in DB (repeatable; use for GDExtension plugins that expose their own classes) |
--godot-version <VER> |
4.6.1 |
Godot version string written into generated Cargo.toml |
--format |
off | Run rustfmt on generated files |
-v, --verbose |
off | Print per-file progress and warnings |
Examples:
# Transpile a single file
gd2rust player.gd -o output -n my_game
# Transpile an entire scripts directory
gd2rust src/scripts/ -o rust_ext -n my_game --format
# Use a different Godot version's API dump (replaces the bundled 4.6.1 DB)
gd2rust player.gd --godot-api ~/godot-4.5/extension_api.json
# Add a third-party plugin's class DB (e.g. GodotSteam) on top of the built-in DB
gd2rust player.gd --extension-api ~/plugins/godotsteam_api.jsonFor a project with player.gd and enemy.gd:
my_game_ext/
├── Cargo.toml # workspace root
├── my_game_ext.gdextension # Godot extension manifest
├── player/
│ ├── Cargo.toml
│ └── src/lib.rs # Player struct + impls
└── enemy/
├── Cargo.toml
└── src/lib.rs # Enemy struct + impls
Build the extension:
cd my_game_ext
cargo build --releaseThe .gdextension manifest in the output points Godot to the compiled library. Copy or symlink the output directory into your Godot project folder.
Input (player.gd):
extends CharacterBody2D
@export var speed: float = 200.0
func _physics_process(delta: float) -> void:
var direction := Input.get_axis("move_left", "move_right")
velocity.x = direction * speed
move_and_slide()Output (player/src/lib.rs):
use godot::prelude::*;
#[derive(GodotClass)]
#[class(base = CharacterBody2D)]
pub struct Player {
#[export]
speed: f64,
base: Base<CharacterBody2D>,
}
#[godot_api]
impl ICharacterBody2D for Player {
fn init(base: Base<CharacterBody2D>) -> Self {
Self { speed: 200.0, base }
}
fn physics_process(&mut self, delta: f64) {
let direction = Input::singleton()
.get_axis("move_left".into(), "move_right".into());
self.base_mut().set_velocity(Vector2::new(direction * self.speed as f32, 0.0));
self.base_mut().move_and_slide();
}
}class_name/extends(defaults toRefCountedwhen omitted)- Instance variables:
var,const,static var - Annotations:
@export,@export_range,@export_enum,@onready,@tool,@warning_ignore - Signals (declaration and
emit_signal) - Inner classes
- Public and private methods
- Static methods
- Virtual lifecycle methods:
_ready,_process,_physics_process,_input,_unhandled_input,_draw,_enter_tree,_exit_tree,_notification,_init - Return types and typed parameters
super.method()calls
if/elif/elseforloops (ranges and iterables)whileloopsmatchstatementsbreak,continue,return,passawait(coroutines viagdext_coroutines)
- Arithmetic, comparison, boolean, and bitwise operators
**(power operator)and,or,notin/not inis/is not(type checks)as(casts)- Ternary expressions
- Lambda /
Callable(basic) - Dictionary literals
int, float, bool, String, Vector2, Vector3, Vector4, Vector2i, Vector3i, Color, Array, Dictionary, Rect2, Transform2D, Transform3D, Basis, Quaternion, Plane, AABB, and packed array types.
print, push_error, len, range, abs, min, max, clamp, sqrt, sin, cos, floor, ceil, round, sign, and common Input, Vector, String, Array, and Dictionary methods.
Named enums and enums with explicit values.
- One-way transpilation. The Rust output becomes the source of truth; there is no sync back to GDScript.
- Godot 4 only. Godot 3 / GDScript 1 is not supported.
- Common subset, not every edge case. Highly dynamic GDScript patterns (e.g. runtime
set()/get(),call(), deeply untyped code) may not transpile correctly and will require manual fixes. - Recursive coroutines emit a compiler warning and require manual rewriting.
- Property setters/getters (
set(value)/get():) have partial support; complex getter/setter bodies may need adjustment. - No GUI or IDE integration. Command-line only.
- No WASM output. The tool targets native GDExtension builds. For web exports, ship the original GDScript unchanged alongside the project — the transpiler does not modify
.gdsource files or the scene tree, so both targets can coexist in the same project. - Basic Rust literacy required. The generated code is idiomatic
gdext, but you will occasionally need to read and edit it.
gd2rust is designed for native builds. The recommended split for projects targeting both platforms:
Native release → gd2rust → Rust GDExtension (.dll / .so)
Web export → GDScript unchanged → Godot web build (.wasm + .pck)
Because the transpiler only adds an extension alongside the project and never modifies .gd files, the same Godot project can produce both builds without any source changes.
The pipeline has five stages:
.gd file(s)
│
▼
Lexer tokenize; emit INDENT / DEDENT tokens
│
▼
Parser recursive-descent → untyped AST
│
▼
Type Inferrer resolve / infer Rust types for every AST node
│
▼
Code Gen emit Rust source using gdext patterns
│
▼
Project Emitter write Cargo.toml, lib.rs, .gdextension manifest
See docs/architecture.md for a full description of each stage and docs/gdscript-mapping.md for the authoritative GDScript-to-Rust reference.
MIT