Built-in types

The so-called "built-in types" or just "builtins" are the basic types that Godot provides. Notably, these are not classes. See also basic built-in types in Godot.

Table of contents

List of types

Here is an exhaustive list of all built-in types, by category. We use the GDScript names; below, we explain how they map to Rust.

Simple types

  • Boolean: bool
  • Numeric: int, float

Composite types

  • Variant (able to hold anything): Variant
  • String types: String, StringName, NodePath
  • Ref-counted containers: Array (Array[T]), Dictionary
  • Packed arrays: Packed*Array for following element types:
    Byte, Int32, Int64, Float32, Float64, Vector2, Vector3, Vector41, Color, String
  • Functional: Callable, Signal

Geometric types

  • Vectors: Vector2, Vector2i, Vector3, Vector3i, Vector4, Vector4i
  • Bounding boxes: Rect2, Rect2i, AABB
  • Matrices: Transform2D, Transform3D, Basis, Projection
  • Rotation: Quaternion
  • Geometric objects: Plane

Miscellaneous

  • Color: Color
  • Resource ID: RID

Rust mapping

Rust types in the gdext API represent the corresponding Godot types in the closest way possible. They are used in parameter and return type position of API functions, for example. They are accessible through godot::builtin, and most symbols are also part of the prelude.

Most builtins have a 1:1 equivalent (e.g. Vector2f, Color etc.). The following list highlights some noteworthy mappings:

GDScript typeRust typeRust example expression
inti642-12345
floatf6423.14159
realreal (either f32 or f64)real!(3.14159)
StringGString"Some string".into()
StringNameStringName"MyClass".into()
NodePathNodePathNodes/MyNode".into()
Array[T]Array<T>array![1, 2, 3]
ArrayVariantArray
or Array<Variant>
varray![1, "two", true]
DictionaryDictionarydict!{"key": "value"}
AABBAabbAabb::new(pos, size)
ObjectGd<Object>Object::new_alloc()
SomeClassGd<SomeClass>Resource::new_gd()
SomeClass (nullable)Option<Gd<SomeClass>>None
Variant (also implicit)VariantVariant::nil()

Note that Godot does not have nullability information in its class API yet. This means that we have to conservatively assume that objects can be null, and thus use Option<Gd<T>> instead of Gd<T> for object return types. This often needs unnecessary unwrapping.

Nullable types are being looked into on Godot side. If there is no upstream solution for a while, we may consider our own workarounds, but it may come with manual annotation of many APIs.

String types

Godot provides three string types: String (GString in Rust), StringName, and NodePath. GString is used as a general-purpose string, while StringName is often used for identifiers like class or action names. The idea is that StringName is cheap to construct and compare.3

These types all support From traits to convert to/from Rust String, and from &str. You can thus use "some_string".into() expressions. If you need more explicit typing, use StringName::from("some_string").

StringName in particular provides a direct conversion from C-string literals such as c"string", introduced in Rust 1.77. This can be used for static C-strings, i.e. ones that remain allocated for the entire program lifetime. Don't use them for short-lived ones.

Arrays and dictionaries

Godot's linear collection type is Array<T>. It is generic over its element type T, which can be one of the supported Godot types (generally anything that can be represented by Variant). A special type VariantArray is provided as an alias for Array<Variant>, which is used when the element type is dynamically typed.

Dictionary is a key-value store, where both keys and values are Variant. Godot currently does not support generic dictionaries, although this feature is under discussion.

Arrays and dictionaries can be constructed using three macros:

#![allow(unused)]
fn main() {
let a = array![1, 2, 3];          // Array<i64>
let b = varray![1, "two", true];  // Array<Variant>
let c = dict!{"key": "value"};    // Dictionary
}

Their API is similar, but not identical to Rust's standard types Vec and HashMap. An important difference is that Array and Dictionary are reference-counted, which means that clone() will not create an independent copy, but another reference to the same instance. Furthermore, since internal elements are stored as variants, they are not accessible by reference. This is why the [] operator (Index/IndexMut traits) is absent, and at() is provided instead, returning by value.

#![allow(unused)]
fn main() {
let a = array![0, 11, 22];

assert_eq!(a.len(), 3);
assert_eq!(a.at(1), 11);         // Panics on out-of-bounds.
assert_eq!(a.get(1), Some(11));  // Also by value, not Some(&11).

let mut b = a.clone();   // Increment reference-count.
b.set(2, 33);            // Modify new ref.
assert_eq!(a.at(2), 33); // Original array has changed.

b.clear();
assert!(b.is_empty());
assert_eq!(b, Array::new()); // new() creates an empty array.
}
#![allow(unused)]
fn main() {
let c = dict! {
    "str": "hello",
    "int": 42,
    "bool": true,
};

assert_eq!(c.len(), 3);
assert_eq!(c.at("str"), "hello".to_variant());    // Panics on missing key.
assert_eq!(c.get("int"), Some(42.to_variant()));  // Option<Variant>, again by value.

let mut d = c.clone();            // Increment reference-count.
d.insert("float", 3.14);          // Modify new ref.
assert!(c.contains_key("float")); // Original dict has changed.
}

To iterate, you can use iter_shared(). This method works almost like iter() on Rust collections, but the name highlights that you do not have unique access to the collection during iteration, since there might exist another reference to the collection. This also means it's your responsibility to ensure that the array/dictionary is not modified in unintended ways during iteration (which should be safe, but may lead to data inconsistencies).

#![allow(unused)]
fn main() {
let a = array!["one", "two", "three"];
let d = dict!{"one": 1, "two": 2.0, "three": Vector3::ZERO};

for elem in a.iter_shared() {
    // elem has type GString.
    println!("Element: {elem}");
}

for (key, value) in d.iter_shared() {
    // key and value both have type Variant.
    println!("Key: {key}, value: {value}");
}
}

Packed arrays

Packed*Array types are used for storing elements space-efficiently ("packed") in contiguous memory. The * stands for the element type, e.g. PackedByteArray or PackedVector3Array.

#![allow(unused)]
fn main() {
// Create from slices.
let bytes = PackedByteArray::from(&[0x0A, 0x0B, 0x0C]);
let ints = PackedInt32Array::from(&[1, 2, 3]);

// Get/set individual elements using Index and IndexMut operators.
ints[1] = 5;
assert_eq!(ints[1], 5);

// Access as Rust shared/mutable slices.
let bytes_slice: &[u8] = b.as_slice();
let ints_slice: &mut [i32] = i.as_mut_slice();

// Access sub-ranges of the array using the same type.
let part: PackedByteArray = bytes.subarray(1, 3); // 1..3, or 1..=2
assert_eq!(part.as_slice(), &[0x0B, 0x0C]);
}

Unlike Array, packed arrays use copy-on-write instead of reference counting. When you clone a packed array, you get a new independent instance. Cloning is cheap as long as you don't modify either instance. Once you use a write operation (anything with &mut self), the packed array will allocate its own memory and copy the data.



Footnotes

1

PackedVector4Array is only available since Godot version 4.3; added in PR #85474.

2

Godot's int and float types are canonically mapped to i64 and f64 in Rust. However, some Godot APIs specify the domain of these types more specifically, so it's possible to encounter i8, u64, f32 etc.

3

When constructing StringName from &str or String, the conversion is rather expensive, since UTF-8 is re-encoded as UTF-32. As Rust recently introduced C-string literals (c"hello"), we can now directly construct from them in case of ASCII. This is more efficient, but keeps memory allocated until shutdown, so don't use it for rarely used temporaries. See API docs and issue #531 for more information.