Migrating to v0.5
This guide covers deprecations and breaking changes when upgrading from godot-rust 0.4 to 0.5.
To reduce the friction, we recommend first updating to the latest patch release of the previous minor version, v0.4.5. Many changes are announced early in the form of deprecation warnings, which contain instructions on how to switch to newer APIs.
You can update your Cargo.toml to the latest patch release by running:
cargo update -p godot
Once you have addressed all deprecation warnings, you can update to the new minor version:
cargo upgrade -p godot
Table of contents
- Godot versions
- Rust 2024 edition
- Required objects in engine APIs
- Property APIs
#[func]boundsIObjectcallback renames- Typed collections
- Core API changes
- Other changes
- Removed deprecated symbols
Godot versions
#1484: godot-rust v0.5 adds API level 4.6, which is now the new default.
This doesn't mean you have to update your Godot version -- just provide Cargo features to use older API levels, e.g. api-4-5.
#1505: API level features are now limited to minor versions only. If you previously used patch-level features like api-4-3-1, you need
to change them to the corresponding minor version, e.g. api-4-3.
Rust 2024 edition
#1500: godot-rust v0.5 uses the Rust 2024 edition.
The minimum supported Rust version (MSRV) is now 1.94.
Required objects in engine APIs
#1383, #1493: GDExtension 4.6 provides non-null ("required") type information for function parameters and return types. This means that
many Rust APIs can now become more type-safe, and your code will not need to deal with Option in cases where values are not supposed to
be null.
If you have Option<Gd> where Godot guarantees non-null objects, you'll need to change this to Gd.
Before (0.4):
#![allow(unused)] fn main() { let maybe_child: Option<Gd<Node>> = ...; node.add_child(&maybe_child); let tree = node.get_tree().unwrap(); let tween = node.create_tween().unwrap(); }
After (0.5):
#![allow(unused)] fn main() { // Option no longer compiles. let definitely_child: Gd<Node> = ...; // non-null node.add_child(&definitely_child); let tree = node.get_tree(); // Panics if not in tree; otherwise get_tree_or_null(). let tween = node.create_tween(); }
Property APIs
Var trait redesign
#1454, #1458, #1466, #1469: The Var trait has been redesigned to support #[var(pub)], which generates typed Rust getters/setters
with clone-based semantics. The trait API changed as follows:
Before (0.4):
#![allow(unused)] fn main() { pub trait Var: GodotConvert { fn get_property(&self) -> Self::Via; fn set_property(&mut self, value: Self::Via); fn var_hint() -> PropertyHintInfo { ... } } }
After (0.5):
#![allow(unused)] fn main() { pub trait Var: GodotConvert { type PubType; fn var_get(field: &Self) -> Self::Via; fn var_set(field: &mut Self, value: Self::Via); fn var_pub_get(field: &Self) -> Self::PubType; fn var_pub_set(field: &mut Self, value: Self::PubType); } }
If you have manual Var implementations, you need to update them. However, for most cases, you can use the new SimpleVar
marker trait instead: types that already support ToGodot + FromGodot + Clone can get an automatic Var implementation via blanket impl.
Before (0.4):
#![allow(unused)] fn main() { impl Var for MyType { fn get_property(&self) -> Self::Via { self.to_godot_owned() } fn set_property(&mut self, value: Self::Via) { *self = FromGodot::from_godot(value); } } }
After (0.5):
#![allow(unused)] fn main() { // MyType: ToGodot + FromGodot + Clone. impl SimpleVar for MyType {} }
#[var] getter/setter semantics
#1454: The #[var(get, set)] keys are now independent from each other.
| Attribute | Old behavior (0.4) | New behavior (0.5) |
|---|---|---|
#[var] | Generates getter + setter | Same as before |
#[var(get)] | Disabled setter | Declares custom get_{field},setter unchanged |
#[var(set)] | Disabled getter | Declares custom set_{field},getter unchanged |
#[var(no_get)] | New; was implied by #[var(set)] | Explicitly disables the getter |
#[var(no_set)] | New; was implied by #[var(get)] | Explicitly disables the setter |
#[var(get = my_fn)] | Custom getter fn | Same as before |
#[var(set = my_fn)] | Custom setter fn | Same as before |
Before (0.4):
#![allow(unused)] fn main() { // Disabled setter, only getter: #[var(get)] my_field: i32, // Disabled getter, only setter: #[var(set)] other_field: f32, }
After (0.5):
#![allow(unused)] fn main() { // Disabled setter, only getter: #[var(no_set)] my_field: i32, // Disabled getter, only setter: #[var(no_get)] other_field: f32, // Custom getter, generated setter: #[var(get)] custom_get_field: i32, }
#[var(pub)] for Rust-side accessors
#1458, #1466: Auto-generated Rust getters/setters for #[var] fields are being phased out.
In v0.4, #[var] fields implicitly generated public Rust accessor methods (e.g. get_my_field() / set_my_field()).
These are now deprecated and will be removed in v0.6.
If you rely on these generated Rust methods, opt in explicitly with #[var(pub)]:
#![allow(unused)] fn main() { #[var(pub)] my_field: i32, // Generates: fn get_my_field(&self) -> i32 // fn set_my_field(&mut self, value: i32) }
The #[var(pub)] accessors use Clone-based semantics (via Var::PubType), while the internal Godot-facing accessors continue to use
GodotConvert::Via.
Compile-time type checks for custom #[var] accessors
#1469: Custom getters and setters declared with #[var(get)] or #[var(set)] are now type-checked at compile time. If your getter's
return type or setter's parameter type doesn't match the field's Var::PubType, you'll get a compile error instead of a runtime failure.
#1518: Additionally, #[var(no_get)] combined with #[export] is now a compile error. Reading a #[var(no_get)] field from GDScript
now panics with a helpful error message instead of silently returning the default value.
Const-qualified getters
#1497: A heuristic now const-qualifies Godot methods prefixed with get_, is_, or has_, changing their receiver from &mut self to
&self. This affects a large number of engine methods, which were unnecessarily declared mutable.
An explicit deny-list handles false positives: methods that actually mutate state remain &mut self.
Examples: FileAccess::get_line() which advances a cursor, StreamPeer::get_8(), etc.
This is a technically breaking change, but existing code using mut bindings will continue to compile. Clippy may emit warnings about
unnecessary mut.
Before (0.4):
#![allow(unused)] fn main() { let mut api: Gd<MultiplayerApi> = ...; let id = api.get_unique_id(); // Required &mut self. }
After (0.5):
#![allow(unused)] fn main() { let api: Gd<MultiplayerApi> = ...; let id = api.get_unique_id(); // Now takes &self. }
GodotShape replaces property hint APIs
#1513, #1514, #1534: The internal representation of how types describe themselves to Godot has been overhauled. A new GodotShape enum
(returned by GodotConvert::godot_shape()) replaces several scattered APIs.
The following trait methods have been removed in favor of a unified GodotConvert::godot_shape():
Var::var_hint()Export::export_hint()GodotType::property_info()GodotType::property_hint_info()GodotType::godot_type_name()GodotType::class_id()Element::element_type_string()PackedElement::element_type_string()
If you had custom Var or Export implementations that overrode var_hint() or export_hint(), you now need to implement
GodotConvert::godot_shape() instead, returning an appropriate GodotShape variant.
This refactor also enables enums in collections. Both engine- and user-defined #[derive(GodotConvert)] enums can now be used
as elements in Array<T> or Dictionary<K, V>.
#[func] bounds
#1435: Parameters and return types in #[func] have been updated. Newly supported are user-defined enums.
Some types have also been restricted -- the following no longer implement ToGodot/FromGodot and thus cannot be passed to
or returned from #[func]:
u64
#1413: Primary reason for disallowing u64 is that GDScript (and Variant) cannot natively store integers other than i64, and it's
very easy to introduce logic errors that silently change the value. See also rationale in the API docs.
If you have existing uses of u64, you can use either:
i64with casting.- any smaller unsigned integer type, if the range is enough.
- a custom type and override
GodotConvert::godot_shape()with param metadata.
Raw pointers and RawPtr<P>
#1436: Raw pointers in engine APIs (native structures, c_void, etc.) are now wrapped in RawPtr<P>, replacing direct
*const T / *mut T parameters. Construction requires unsafe, which wasn't the case before -- you could previously return a pointer to a
local variable from #[func] without ever typing unsafe.
Before (0.4):
#![allow(unused)] fn main() { #[func] fn pass_native_struct(&self, caret_info: *const CaretInfo) -> VarDictionary { let info = unsafe { &*caret_info }; // use &CaretInfo directly. } #[func] fn native_struct_array_ret(&self) -> *const Glyph { self.some_raw_ptr } }
Besides the native-structure types, the following raw pointer impls for ToGodot/FromGodot have been removed:
*const std::ffi::c_void*mut std::ffi::c_void*mut *const u8*mut i32*mut f64*mut u8*const u8
After (0.5):
#![allow(unused)] fn main() { use godot::meta::conv::RawPtr; #[func] fn pass_native_struct(&self, caret_info: RawPtr<*const CaretInfo>) -> VarDictionary { let caret_raw: *const CaretInfo = caret_info.ptr(); let info = unsafe { &*caret_raw }; // use &CaretInfo directly. } #[func] fn native_struct_array_ret(&self) -> RawPtr<*const Glyph> { // SAFETY: caller must ensure returning pointer to Godot is safe. unsafe { RawPtr::new(self.some_raw_ptr) } } }
IObject callback renames
#1527: Virtual callbacks in the IObject trait (and related I* traits inheriting from Object) are now prefixed with on_, to
avoid naming collisions with same-named methods on Object and to clarify that these are callbacks.
| Old name | New name |
|---|---|
get_property() | on_get() |
set_property() | on_set() |
get_property_list() | on_get_property_list() |
validate_property() | on_validate_property() |
The old names are deprecated and will continue to function until v0.6.
Typed collections
Typed Dictionary<K, V>
#1502, #1507, #1516: Dictionaries are now generic over key and value types: Dictionary<K, V>. This mirrors the typed Array<T> API.
Dictionary<K, V>is the typed dictionary.AnyDictionaryis a type-erased dictionary (analogous toAnyArray).VarDictionaryis now an alias forDictionary<Variant, Variant>.- Engine APIs now accept
&AnyDictionaryinstead of&VarDictionary, so you can pass any&Dictionary<K, V>directly. - Typed dictionary iterators are available:
dict.iter_shared()returns(K, V)pairs.
This is mostly a backwards-compatible change as VarDictionary still exists, however there are some places which need attention.
Methods that accept values have changed bounds from ToGodot to AsArg<Variant>, as a special case of AsArg<K>/AsArg<V>.
Elements whose ToGodot::Pass is not ByValue (e.g. objects, callables) now need to be passed by reference:
Before (0.4):
#![allow(unused)] fn main() { dict.set("hello", gd); }
After (0.5):
#![allow(unused)] fn main() { dict.set("hello", &gd); }
vdict! macro uses => separator
#1523: The vdict! macro now uses => instead of : to separate keys from values. This avoids ambiguity with Rust's
token tree parsing and removes the need to wrap complex key expressions in parentheses. It's also more in line with crates like maplit.
Before (0.4):
#![allow(unused)] fn main() { let tiles = vdict! { (Vector2i::new(1, 2)): Tile::GRASS, (Vector2i::new(1, 3)): Tile::WATER, }; }
After (0.5):
#![allow(unused)] fn main() { let tiles = vdict! { Vector2i::new(1, 2) => Tile::GRASS, Vector2i::new(1, 3) => Tile::WATER, }; }
In good old godot-rust fashion, the : syntax continues to be supported during a transition period (with deprecation warnings).
iarray! and idict! macros for type inference
#1530: godot-rust v0.5 underwent a significant refactoring to maintain decent Variant support in Dictionary<K, V>. The short story is
that we can no longer rely on K/V being Variant, so we had to replace ToGodot with the more general AsArg<K/V> bounds. This comes at
a cost: due to more candidates for AsArg conversions, type inference is ambiguous in situations where it cannot be deduced from the context
(like function return types).
#![allow(unused)] fn main() { let d = dict! { "key": 10 }; // Error: "key" could be GString, StringName or Variant. // dict! now needs explicit type annotations: let d: Dictionary<GString, i32> = dict! { "key": 10 }; }
Typing three extra identifiers is of course beyond the acceptable, so we have a solution.
New macros iarray! and idict! ("inferred") create arrays and dictionaries without explicit type hints.
They use opinionated but unambiguous type inference. For example, iarray!["hello"] infers Array<GString>, never
Array<StringName>. Not all element types are supported as of now.
The existing array! and dict! macros remain unchanged and still require type context (annotation, function parameter, etc.).
#![allow(unused)] fn main() { // No type annotation needed: let ints = iarray![3, 1, 4]; // Array<i32> let strs = iarray!["a", "b"]; // Array<GString> let d = idict! { "key" => 10 }; // Dictionary<GString, i32> // array!/dict! still work with type context: let ints: Array<i64> = array![3, 1, 4]; }
AnyArray -- covariant typed/untyped array
#1422, #1434: A new AnyArray type provides a type-erased array that can store either typed Array<T> or untyped VarArray. It is
covariant: any &Array<T> can be used where &AnyArray is expected, thanks to Deref coercion.
#![allow(unused)] fn main() { use godot::builtin::{Array, AnyArray, VarArray}; let typed: Array<i64> = array![1, 2, 3]; // Deref coercion: use typed array where AnyArray is expected. let any_ref: &AnyArray = &typed; assert_eq!(any_ref.len(), 3); // Explicit upcast for owned values. let any: AnyArray = typed.clone().upcast_any_array(); // Downcast back to typed. let back: Array<i64> = any.try_cast_array::<i64>() .expect("convert back to typed"); }
Engine APIs that previously accepted &VarArray parameters (like Callable::callv()) now take &AnyArray, which means you can pass any
&Array<T> directly without manual conversion. If you were explicitly constructing a VarArray to pass to such methods, note that
&VarArray coerces to &AnyArray via Deref, so no changes should be necessary in most cases.
For owned values (e.g. returned by virtual functions), you can use the upcast_any_array() method to convert an Array<T> into an AnyArray.
Core API changes
Builder APIs for builtin methods
#1424: Builtin types (GString, Vector2, Color, etc.) now support the _ex() builder pattern for methods with default parameters,
consistent with how engine class methods already work.
Before (0.4):
#![allow(unused)] fn main() { let pi = GString::num(std::f64::consts::PI, 3); }
After (0.5):
#![allow(unused)] fn main() { let pi = GString::num_ex(std::f64::consts::PI).decimals(3).done(); }
The non-_ex variants remain available and use Godot's default parameter values.
In v0.4, we often emulated the builder pattern -- inconsistently at that:
- by requiring all parameters
- with multiple methods
- with our handcrafted builder
If you used such methods, your code will need to be updated to the new pattern.
Type-safe duplicate_node() and duplicate_resource()
#1492: New generic duplication methods return the concrete type instead of requiring manual downcasts.
#![allow(unused)] fn main() { let node: Gd<Node2D> = Node2D::new_alloc(); let copy: Gd<Node2D> = node.duplicate_node(); // Directly typed. // Builder pattern for fine-grained control: let copy = node.duplicate_node_ex() .flags(DuplicateFlags::SIGNALS | DuplicateFlags::GROUPS) .done(); let resource: Gd<Resource> = Resource::new_gd(); let copy: Gd<Resource> = resource.duplicate_resource(); // Deep duplication (Godot 4.5+): let deep = resource.duplicate_resource_ex() .deep(DeepDuplicateMode::ALL) .done(); }
The old Node::duplicate() and Resource::duplicate() methods are deprecated in favor of these new type-safe variants.
Geometric API changes
#1447, #1461, #1463: Several geometric types gained new methods:
Aabb::get_endpoint()-- returns a corner vertex of the bounding box.Aabbcleanup: additional methods likeget_center()andget_support()(previouslysupport()).Rect2/Rect2i: corner constructors and additional utility methods.
The from_corners() constructors on Rect2, Rect2i, and Aabb have been deprecated in favor of
from_position_end(), which better describes the parameters (position + end point, not two arbitrary corners).
Module reorganization
#1531, #1537: The meta, register, and signal modules have been reorganized. If you import specific items from these modules,
your import paths may need updating:
| Symbol(s) | Old module (0.4) | New module (0.5) |
|---|---|---|
GodotShape, GodotElementShape, Enumerator | godot::meta | godot::meta::shape |
ByValue, ByRef, ByObject, ArgPassing | godot::meta | godot::meta::conv |
ElementType | godot::meta | godot::meta::inspect |
GodotConvert (derive macro) | godot::register | godot::meta |
Export, Var (derive macros) | godot::register | godot::register::property |
TypedSignal, ConnectHandle, signal types | godot::register | godot::signal |
PropertyHintInfo, PropertyInfo, MethodInfo | godot::meta | godot::register::info |
Most commonly used symbols remain available in godot::prelude and are not affected.
Trait renames: ArrayElement and PackedArrayElement
#1506: The traits ArrayElement and PackedArrayElement have been renamed to Element and PackedElement, respectively.
If you use these traits as bounds in generic code, update the names accordingly.
Other changes
GFile::read_as_gstring_entire() parameter removed
#1494: The skip_cr parameter has been removed from GFile::read_as_gstring_entire() to provide a uniform API across Godot versions.
Before (0.4):
#![allow(unused)] fn main() { let text = gfile.read_as_gstring_entire(true)?; }
After (0.5):
#![allow(unused)] fn main() { let text = gfile.read_as_gstring_entire()?; }
GString, StringName and NodePath: removed arg() method
The .arg() methods on GString and StringName have been removed. They were used to convert between Godot string types when
passing arguments to functions expecting a different string type. The idea is to come up with a more general AsArg conversion method,
possibly reserving the .arg() name for that in the future.
To migrate, use T::from():
Before (0.4):
#![allow(unused)] fn main() { // Node::is_in_group() takes impl AsArg<StringName> let group: GString = some_group_name(); node.is_in_group(group.arg()); }
After (0.5):
#![allow(unused)] fn main() { let group: GString = some_group_name(); node.is_in_group(&StringName::from(&group)); }
Environment variable renames
#1521: All environment variables now use a uniform GDRUST_ prefix. The old names are still recognized but emit a deprecation warning.
| Old name | New name |
|---|---|
GODOT4_BIN | GDRUST_GODOT_BIN |
GODOT4_GDEXTENSION_JSON | GDRUST_GODOT_API_JSON |
GODOT_RUST_NOWARN | GDRUST_SUPPRESSED_WARNINGS |
| (new in v0.5) | GDRUST_MAIN_EXTENSION |
Removed deprecated symbols
#1459: All symbols that were deprecated during the v0.4 cycle have been removed. If you have already addressed all deprecation warnings in v0.4.5, you should be covered for the most part. The following table lists the removed symbols and their replacements:
| Removed symbol | Replacement |
|---|---|
VariantArray (type alias) | VarArray |
ClassName (type alias) | ClassId |
GodotClass::class_name() | GodotClass::class_id() |
WithBaseField::apply_deferred() | run_deferred() + run_deferred_gd() |
Gd::apply_deferred() | Gd::run_deferred() + Gd::run_deferred_gd() |
Callable::from_local_fn() | Callable::from_fn() |
Callable::from_local_static() | Callable::from_class_static() |
*::hash() (Godot hash value) | *::hash_u32() |
Array::bsearch_custom() | Array::functional_ops().bsearch_custom() |
ExtensionLibrary::on_level_init() | ExtensionLibrary::on_stage_init() |
ExtensionLibrary::on_level_deinit() | ExtensionLibrary::on_stage_deinit() |
ExtensionLibrary::on_main_loop_startup() | on_stage_init(InitStage::MainLoop) |
ExtensionLibrary::on_main_loop_shutdown() | on_stage_deinit(InitStage::MainLoop) |
#[class(no_init, base=EditorPlugin)] (warning) | Now a hard compile error; use #[class(init)] |