This page covers the foundational data types that underpin Godot's data model at the C++ layer: String, StringName, NodePath, Array, Dictionary, and the Variant container that unifies them all. These types are defined under core/string/ and core/variant/ and are used throughout every engine subsystem.
For the Object class and its property/signal system that builds on top of these types, see Object & Signal System. For the GDScript runtime that exposes these types to scripts, see GDScript Runtime & Cache.
Diagram: Core type hierarchy and their implementation files
Sources: core/variant/variant.h93-146 core/string/ustring.h266-268 core/variant/array.h47-48 core/variant/array.cpp45-54 core/variant/dictionary.cpp47-54
String is Godot's primary string type, defined in core/string/ustring.h and implemented in core/string/ustring.cpp
String is internally a CowData<char32_t> — a copy-on-write array of 32-bit Unicode code points. The key consequence: every string assignment is cheap (just a reference increment), and mutation triggers a copy only when shared.
class String {
CowData<char32_t> _cowdata;
static constexpr char32_t _replacement_char = 0xfffd;
};
U+FFFD.Sources: core/string/ustring.h266-269 core/string/ustring.cpp161-182
String provides static factory methods and instance methods for all encoding round-trips:
| Method / Direction | Description |
|---|---|
String::utf8(const char*) | Construct from UTF-8 byte string |
String::utf16(const char16_t*) | Construct from UTF-16 |
to_utf8_buffer() | Export as PackedByteArray of UTF-8 bytes |
to_utf16_buffer() | Export as PackedByteArray of UTF-16 bytes |
append_latin1(Span<char>) | Append Latin-1 (each byte → char32_t) |
append_utf32(Span<char32_t>) | Append with Unicode validity check |
The method append_utf32 validates surrogate pairs and out-of-range code points, substituting _replacement_char on error and returning ERR_PARSE_ERROR.
Sources: core/string/ustring.cpp160-220
String provides multiple comparison functions for different sorting contexts:
| Method | Behavior |
|---|---|
casecmp_to | Case-sensitive, Unicode code-point order |
nocasecmp_to | Case-insensitive (uses _find_upper table) |
naturalcasecmp_to | Natural sort — numeric runs compared numerically |
naturalnocasecmp_to | Natural sort, case-insensitive |
filecasecmp_to | Like natural, but . and _ sort first (for filenames) |
filenocasecmp_to | File sort, case-insensitive |
All comparison methods return signed char (-1, 0, 1).
Sources: core/string/ustring.cpp443-700
String supports casing/identifier transformations:
| Method | Example |
|---|---|
to_upper() / to_lower() | "hello" → "HELLO" |
capitalize() | "move_local_x" → "Move Local X" |
to_camel_case() | "move_local_x" → "moveLocalX" |
to_pascal_case() | "move_local_x" → "MoveLocalX" |
to_snake_case() | "MoveLocalX" → "move_local_x" |
to_kebab_case() | "MoveLocalX" → "move-local-x" |
All of these route through the internal _separate_compound_words() helper, which splits on transitions between uppercase, lowercase, and digits.
Sources: core/string/ustring.cpp702-807
| Method | Description |
|---|---|
split(delim, allow_empty, maxsplit) | Forward split into Vector<String> |
rsplit(delim, allow_empty, maxsplit) | Reverse split |
split_spaces(maxsplit) | Split on whitespace (char < 33) |
get_slice(delim, index) | Get one slice without building full array |
get_slicec(char32_t, index) | get_slice variant for a single char delimiter |
join(parts) | Join a Vector<String> with this string as separator |
split_floats / split_ints | Split and convert to numeric vectors |
Sources: core/string/ustring.cpp970-1345
String has bidirectional conversion with all numeric types:
to_int(), to_float(), to_float64() — parse string to numberString::num(double, decimals) — format number, removing trailing zerosString::num_int64(int64_t, base, capitalize) — integer to string in any basehex_to_int(), bin_to_int() — parse hexadecimal or binary literalsis_valid_int(), is_valid_float(), is_valid_hex_number() — validation predicatesSources: core/string/ustring.cpp1393-1457
For interoperating with C APIs, CharStringT<T> (aliased as CharString for char and Char16String for char16_t) is a narrow-encoding buffer also backed by CowData. It is returned by String::utf8() and String::utf16().
CharProxy<T> is a write-through proxy returned by String::operator[](int). It allows str[i] = 'x' while still enforcing copy-on-write semantics.
Sources: core/string/ustring.h129-254
StringName is an interned, immutable string for use as identifiers — property names, method names, signal names, class names, and group names. Defined in core/string/string_name.h.
StringName objects with the same text are guaranteed to point to the same internal node.StringNameData nodes.From the documentation:
Two
StringNames with the same value are the same object. Comparing them is extremely fast compared to regularStrings.
| Syntax | Context |
|---|---|
StringName("example") | C++ or GDScript constructor |
&"example" | GDScript literal syntax (compile-time interning) |
Implicit from String | Most engine APIs accept String and auto-convert |
StringName is used wherever a string is used as an identifier that will be compared frequently:
ClassDB uses StringName for all class, method, and property names.Object::call, Object::set, Object::get all accept StringName.StringName.Sources: doc/classes/StringName.xml1-12 core/variant/variant.h124
NodePath is a pre-parsed path through the scene tree. Defined in core/string/node_path.h.
A NodePath is composed of:
/):)/)Examples:
| Path | Meaning |
|---|---|
^"A/B" | Child A, then its child B |
^"/root/Title" | Absolute path from SceneTree root |
^"../C" | Sibling node C |
^"Node:position:x" | Property position.x on Node |
get_name(idx) — Returns the node name at the given index.get_subname(idx) — Returns the subname (property) at the given index.get_name_count() / get_subname_count() — Path segment counts.is_absolute() — Whether path starts from the tree root.get_concatenated_names() / get_concatenated_subnames() — Reconstruct the slash/colon portions.NodePath can be constructed from a String and converts back with String(NodePath). In Variant, it is stored with its own type tag NODE_PATH.
Sources: doc/classes/NodePath.xml1-30 core/variant/variant.h125
Array is a reference-counted, dynamically-sized sequence of Variant values. Defined in core/variant/array.h and implemented in core/variant/array.cpp.
struct ArrayPrivate {
SafeRefCount refcount;
Vector<Variant> array;
Variant *read_only; // non-null if array is read-only
ContainerTypeValidate typed; // type constraint for typed arrays
};
Assignment copies the ArrayPrivate* pointer and increments the refcount. Writes trigger a copy only when the refcount is greater than one.
Sources: core/variant/array.cpp45-54 core/variant/array.cpp56-86
An Array can be typed by providing a ContainerTypeValidate specifying a Variant::Type, optional class name, and optional script. When typed:
Array(base, type, class_name, script) creates a typed array from an untyped one.In GDScript, the syntax var a: Array[int] = [] produces a typed array.
When a read-only flag is set (via internal engine mechanisms), element access through operator[] returns a pointer to a temporary Variant rather than the live array slot. Write attempts are rejected with an error.
Sources: core/variant/array.cpp104-114 core/variant/array.cpp124-127
| Method | Description |
|---|---|
push_back / append | Add to end |
push_front / pop_front | Queue-style front access |
insert(pos, val) | O(n) insert at position |
remove_at(pos) | O(n) removal |
erase(val) | Find-and-remove first occurrence |
find(val, from) | Linear search |
bsearch(val, before) | Binary search on sorted array |
sort() / sort_custom(callable) | In-place sort |
filter(callable) | Returns new filtered array |
map(callable) | Returns new mapped array |
reduce(callable, accum) | Fold/accumulate |
duplicate(deep) | Shallow or deep copy |
Sources: doc/classes/Array.xml166-800
For performance-critical homogeneous data, Godot provides packed arrays as typedef Vector<T> aliases:
| Type alias | Element type |
|---|---|
PackedByteArray | uint8_t |
PackedInt32Array | int32_t |
PackedInt64Array | int64_t |
PackedFloat32Array | float |
PackedFloat64Array | double |
PackedStringArray | String |
PackedVector2Array | Vector2 |
PackedVector3Array | Vector3 |
PackedColorArray | Color |
PackedVector4Array | Vector4 |
These are stored inside Variant as PackedArrayRef<T> wrapper objects with their own reference counting, separate from Array.
Sources: core/variant/variant.h81-91 core/variant/variant.h218-244
Dictionary is a reference-counted hash map from Variant keys to Variant values. Defined in core/variant/dictionary.h and implemented in core/variant/dictionary.cpp.
struct DictionaryPrivate {
SafeRefCount refcount;
Variant *read_only;
HashMap<Variant, Variant,
HashMapHasherDefault,
StringLikeVariantComparator> variant_map;
ContainerTypeValidate typed_key;
ContainerTypeValidate typed_value;
Variant *typed_fallback;
};
The comparator StringLikeVariantComparator treats String and StringName as equivalent keys — a StringName key and a String key with the same text will collide.
Insertion order is preserved (the underlying HashMap implementation maintains a linked list of entries).
Sources: core/variant/dictionary.cpp47-54
Like Array, Dictionary supports typed keys and/or typed values. The constructor Dictionary(base, key_type, key_class_name, key_script, value_type, value_class_name, value_script) creates a typed dictionary.
In GDScript: var d: Dictionary[String, int] = {}.
| Method | Description |
|---|---|
operator[](key) | Get/set by key; creates entry on write |
get(key, default) | Get with fallback value |
has(key) | Key existence check |
has_all(keys) | Check multiple keys at once |
erase(key) | Remove a key |
keys() | Returns Array of all keys |
values() | Returns Array of all values |
merge(other, overwrite) | Merge another dictionary in |
duplicate(deep) | Shallow or deep copy |
find_key(value) | Reverse lookup |
Sources: doc/classes/Dictionary.xml178-600 core/variant/dictionary.cpp56-300
Variant is the universal container that can hold any Godot type. Defined in core/variant/variant.h.
Variant::Type covers all 40+ types:
enum Type {
NIL,
BOOL, INT, FLOAT, STRING,
VECTOR2, VECTOR2I, RECT2, RECT2I,
VECTOR3, VECTOR3I, TRANSFORM2D,
VECTOR4, VECTOR4I, PLANE, QUATERNION,
AABB, BASIS, TRANSFORM3D, PROJECTION,
COLOR, STRING_NAME, NODE_PATH, RID,
OBJECT, CALLABLE, SIGNAL,
DICTIONARY, ARRAY,
PACKED_BYTE_ARRAY, PACKED_INT32_ARRAY, PACKED_INT64_ARRAY,
PACKED_FLOAT32_ARRAY, PACKED_FLOAT64_ARRAY, PACKED_STRING_ARRAY,
PACKED_VECTOR2_ARRAY, PACKED_VECTOR3_ARRAY,
PACKED_COLOR_ARRAY, PACKED_VECTOR4_ARRAY,
VARIANT_MAX
};
Sources: core/variant/variant.h96-146
A Variant is 24 bytes (with real_t = float) or 40 bytes (with real_t = double). The value is stored in a discriminated union:
union {
bool _bool;
int64_t _int;
double _float;
Transform2D* _transform2d; // heap-allocated
AABB* _aabb; // heap-allocated
Basis* _basis; // heap-allocated
Transform3D* _transform3d; // heap-allocated
Projection* _projection; // heap-allocated
PackedArrayRefBase* packed_array;
void* _ptr;
uint8_t _mem[...]; // inline storage for small types
} _data;
Small types (bool, int, float, Vector2/3/4, Color, RID, etc.) fit in _mem inline. Larger types (Transform2D, AABB, Basis, etc.) are heap-allocated on construction.
The needs_deinit static array indicates which types require cleanup on destruction.
Sources: core/variant/variant.h163-312
Diagram: Variant storage layout by type category
Sources: core/variant/variant.h250-262 core/variant/variant.h268-312
Variant supports implicit and explicit conversions between types. The method Variant::can_convert(from, to) encodes the lossless conversion table:
| Target type | Allowed source types |
|---|---|
BOOL | INT, FLOAT, STRING |
INT | BOOL, FLOAT, STRING |
FLOAT | BOOL, INT, STRING |
STRING | All except OBJECT |
STRING_NAME | STRING |
NODE_PATH | STRING |
ARRAY | Any PACKED_*_ARRAY |
PACKED_*_ARRAY | ARRAY |
VECTOR2 | VECTOR2I |
QUATERNION | BASIS |
can_convert_strict is a subset that disallows lossy conversions (e.g., STRING → numeric).
Sources: core/variant/variant.cpp189-530
Arithmetic, comparison, and logical operators on Variant values are dispatched through per-type function tables registered in variant_op.cpp. The Variant::evaluate(op, a, b, result, valid) function selects the correct evaluator for a given (Type, Type) pair.
Built-in method calls on Variant values (e.g., "hello".to_upper() in GDScript) are dispatched through tables registered in variant_call.cpp via the METHOD_CLASS, STATIC_METHOD_CLASS, and VARARG_CLASS macros.
Sources: core/variant/variant_op.cpp33-40 core/variant/variant_call.cpp372-410
Diagram: Variant dispatch infrastructure
Sources: core/variant/variant_call.cpp372-411 core/variant/variant_setget.cpp36-60 core/variant/variant_op.cpp33-40
These map directly to C++ native types inside Variant:
| Godot type | C++ storage | Notes |
|---|---|---|
bool | bool _bool | In boolean context: false if zero |
int | int64_t _int | Signed 64-bit; wraps on overflow |
float | double _float | 64-bit double; precision ~14 decimal digits |
Variant auto-converts between these three without an explicit cast when can_convert allows it. At the scripting layer, GDScript performs type coercion; at the C++ layer, explicit casts are required.
Sources: core/variant/variant.h98-103 doc/classes/int.xml1-12 doc/classes/float.xml1-10
All Variant type infrastructure is registered during engine startup through a sequence of calls in Variant:
Variant::_register_variant_operators() // variant_op.cpp
Variant::_register_variant_methods() // variant_call.cpp
Variant::_register_variant_setters_getters() // variant_setget.cpp
Variant::_register_variant_constructors() // variant_construct.cpp
Variant::_register_variant_destructors() // variant_construct.cpp
Variant::_register_variant_utility_functions()
These are called during engine initialization (see Engine Initialization) and their unregister counterparts are called during shutdown.
The TypedArray<T> and TypedDictionary<K,V> templates in core/variant/typed_array.h and core/variant/typed_dictionary.h are thin wrappers around Array and Dictionary used in the C++ API to carry type information at compile time, without changing the runtime representation.
Sources: core/variant/variant.h321-332 core/variant/typed_array.h1-50 core/variant/typed_dictionary.h1-50
All core types participate in Godot's hash system. The file core/templates/hashfuncs.h provides the hash primitives:
hash_djb2 / hash_djb2_buffer — DJB2 for byte sequenceshash_murmur3_one_32 — Murmur3 mix stephash_fmix32 — Murmur3 finalizationmake_uint64_t / hash_one_uint64 — 64-bit hash utilitiesString::hash() returns a 32-bit DJB2 hash. StringName caches its hash at construction. Array::recursive_hash and Dictionary::recursive_hash walk the container, capping at MAX_RECURSION = 1024 depth.
The Variant hash is computed by Variant::hash() and dispatches per type.
Sources: core/templates/hashfuncs.h1-50 core/variant/array.cpp191-208 doc/classes/String.xml365-372
| File | Primary type defined |
|---|---|
core/string/ustring.h / .cpp | String, CharStringT, CharProxy |
core/string/string_name.h / .cpp | StringName |
core/string/node_path.h / .cpp | NodePath |
core/variant/array.h / .cpp | Array, ArrayPrivate |
core/variant/dictionary.h / .cpp | Dictionary, DictionaryPrivate |
core/variant/variant.h / .cpp | Variant, Variant::Type enum |
core/variant/variant_call.cpp | Variant method dispatch tables |
core/variant/variant_op.cpp | Variant operator dispatch tables |
core/variant/variant_setget.cpp | Variant member getter/setter tables |
core/variant/variant_construct.cpp | Variant constructors/destructors |
core/variant/typed_array.h | TypedArray<T> |
core/variant/typed_dictionary.h | TypedDictionary<K,V> |
core/templates/cowdata.h | CowData<T> (used by String) |
core/templates/hashfuncs.h | Hash primitive functions |
Refresh this wiki
This wiki was recently refreshed. Please wait 3 days to refresh again.