# String vs StringName vs NodePath in gdext-nim: Key Differences and When to Use Each

> Understand String vs StringName vs NodePath in gdext-nim. Learn when to use each for efficient text handling, fast lookups, and scene tree navigation.

- Repository: [godot-nim 4+/gdext-nim](https://github.com/godot-nim/gdext-nim)
- Tags: deep-dive
- Published: 2026-03-02

---

**String handles general-purpose text, StringName provides immutable hashed identifiers for O(1) lookups, and NodePath stores hierarchical scene tree references for runtime node resolution.**

The gdext-nim library provides Nim bindings for Godot 4’s extension system, exposing three distinct string-related types that serve fundamentally different purposes. Choosing the correct type—**String**, **StringName**, or **NodePath**—impacts both performance and correctness when your Nim code interacts with the Godot engine.

## Core Architectural Differences

Each type maps directly to a Godot 4 core engine class but offers different performance characteristics and semantic meaning in gdext-nim.

### String: General-Purpose UTF-8 Text

**String** is a full-featured, mutable Unicode string that stores arbitrary text content. According to the source code in `src/gdext/stringtools.nim` (lines 5–7), you create instances using the `newString` constructor:

```nim
proc newString*(): String
proc newString*(str: string): String

```

Use **String** for user-visible text, file I/O, concatenation, and formatting operations. It supports the `%` operator for formatting and provides complete UTF-8 manipulation capabilities. However, because it stores the full character data, equality comparisons require byte-by-byte checking, making it slower than hashed alternatives for identifier comparisons.

### StringName: Hashed, Interned Identifiers

**StringName** represents an immutable, interned string that computes and caches its hash upon creation. The implementation in `src/gdext/stringtools.nim` (lines 8–10) wraps Godot’s native `StringName` type:

```nim
proc newStringName*(): StringName
proc newStringName*(str: string): StringName

```

Godot uses this type internally for property names, method names, signal names, class identifiers, and enum values. Because the hash is pre-computed, equality checks become O(1) operations rather than O(n) string comparisons. Use **StringName** when storing identifiers, dictionary keys, or any values where you perform frequent equality tests.

### NodePath: Hierarchical Scene References

**NodePath** stores a path string that points to a node within the scene tree (e.g., `"Player/Camera"`). Defined in `src/gdext/stringtools.nim` (lines 11–13), it wraps Godot’s `NodePath` class:

```nim
proc newNodePath*(): NodePath
proc newNodePath*(str: string): NodePath

```

Unlike a simple string, **NodePath** provides path-specific methods such as `isAbsolute`, `getName`, and `slice` (see the generated API in `src/gdext/gen/gdnodepath.nim`). Godot resolves these paths at runtime to concrete `Node` instances. Use this type for exported node references, arguments to `getNode()`, or serialized node paths.

## Implicit Conversions from Native Nim Strings

The gdext-nim bindings provide convenience converters in `src/gdext/sugars.nim` that allow seamless assignment from Nim’s native `string` type:

```nim

# src/gdext/sugars.nim

converter convertToStringName*(str: string): StringName = newStringName(str)  # Lines 15-16

converter convertToNodePath*(str: string): NodePath = newNodePath(newGdString str)  # Lines 16-17

```

These converters enable natural syntax where the compiler automatically constructs the appropriate Godot type:

```nim
import gdext

let identifier: StringName = "health"      # Implicitly calls newStringName

let cameraPath: NodePath = "Player/Camera" # Implicitly calls newNodePath

```

## Practical Usage Examples

### Using String for Display and Formatting

For arbitrary text manipulation and user-facing content, instantiate **String** explicitly:

```nim
import gdext

let message = newString("Player score: %d" % 100)
echo $message  # Converts back to Nim string for display

```

This leverages the constructors defined in `src/gdext/stringtools.nim`.

### Using StringName for Dictionary Keys

When working with Godot’s Dictionary type, **StringName** provides optimal lookup performance:

```nim
import gdext

var stats = newDictionary[StringName, int]()
stats["max_health"] = 100   # Implicit conversion via convertToStringName

stats["current_health"] = 80

# Fast O(1) hash-based lookup

let maxHp = stats["max_health"]

```

Godot expects **StringName** keys for property bag patterns and internal metadata lookups.

### Using NodePath for Scene Navigation

Reference nodes using **NodePath** with the `/` operator overload defined in `src/gdext/objecttools.nim` (lines 63–64):

```nim
import gdext

let player = instantiate(Node, "Player")
let cameraPath: NodePath = "Player/Camera"
let camera = player.getNode(cameraPath)  # Runtime resolution

# Alternative syntax using the / operator

let cam = player / cameraPath

```

### Exporting NodePath Properties

Export **NodePath** variables to the Godot editor to allow designers to wire up references:

```nim
import gdext

type MyComponent = ref object of Node
  targetNode* {.gdexport.}: NodePath   # Appears in inspector

# In editor, set targetNode to "UI/HealthBar"

```

This pattern appears in the test suite at `testproject/editor/nim/src/classes/gdproptestnode_pragmas.nim` (lines 46–48).

## Performance and Memory Considerations

| Type | Comparison Speed | Memory Overhead | Best For |
|------|-----------------|-----------------|----------|
| **String** | O(n) byte comparison | Full text storage | User text, formatting, file content |
| **StringName** | O(1) hash comparison | Interned (shared) instances | Identifiers, property names, dictionary keys |
| **NodePath** | O(n) path parsing | Path string + cached segments | Scene references, node lookup |

**StringName** interns identical strings, meaning multiple variables with the same value share the same underlying Godot object, reducing memory pressure for frequently used identifiers like `"position"` or `"visible"`.

## Summary

- **String** (`src/gdext/stringtools.nim`) handles general text, file operations, and formatting using standard UTF-8 encoding.
- **StringName** (`src/gdext/stringtools.nim`) provides hashed, immutable identifiers optimized for property names, signal names, and dictionary keys with O(1) equality checks.
- **NodePath** (`src/gdext/stringtools.nim`) stores scene tree paths resolved at runtime via `getNode()`, supporting exports and the `/` operator from `src/gdext/objecttools.nim`.
- Use implicit converters in `src/gdext/sugars.nim` to assign native Nim strings directly to **StringName** and **NodePath** variables.

## Frequently Asked Questions

### Can I convert between String, StringName, and NodePath?

Yes. While implicit converters handle `string` to **StringName** or **NodePath**, you can explicitly construct any type from another using the `newString`, `newStringName`, and `newNodePath` constructors. For example, create a **NodePath** from a **String** using `newNodePath(myString)`.

### Why should I use StringName instead of String for property names?

**StringName** pre-computes a hash value and interns the string data, making equality comparisons O(1) instead of O(n). Godot’s internal APIs expect **StringName** for method and property lookups, so using the correct type avoids implicit conversions and improves performance when calling `setNamed()` or accessing dictionary entries.

### When should I use NodePath versus storing a Node reference directly?

Use **NodePath** when you need to reference nodes that might not exist at initialization time or when exposing references to the editor via `{.gdexport.}`. Store direct `Node` references when you access the same node frequently in code and guarantee its existence, as this avoids the runtime path resolution overhead of `getNode()`.

### How does the `/` operator work with NodePath?

The `/` operator defined in `src/gdext/objecttools.nim` (lines 63–64) serves as a convenience alias for `getNode()`. When you write `player / "Camera"`, gdext-nim implicitly converts the string to **NodePath** and calls `getNode(player, cameraPath)`, returning the resolved `Node` or `nil` if the path is invalid.