# Understanding the Difference Between Object, RefCounted, and Node in gdext’s Type Hierarchy

> Understand gdext-nim object, RefCounted, and Node differences. Learn about reference counting for memory safety and scene tree lifecycle management for Nodes. Optimize resource handling.

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

---

**In gdext-nim, `RefCounted` objects use automatic reference counting via `GdRef[T]` wrappers for memory-safe resource management, while `Node` objects participate in Godot’s scene tree lifecycle without reference counting, requiring manual tree management via `add_child` and `queue_free`.**

The `godot-nim/gdext-nim` repository provides Nim bindings for Godot 4.x that faithfully mirror the engine’s class hierarchy. At the root sits `Object`, but the critical architectural split occurs between `RefCounted` and `Node` branches—each representing fundamentally different memory management philosophies that determine how you instantiate, store, and lifecycle-manage objects in your Nim code.

## The Foundation: Object as the Root

All engine-exposed types in gdext ultimately derive from `Object`, defined in `src/gdext/builtinindex.nim`. This base type provides the low-level method binding infrastructure used by both branches. However, `Object` itself is rarely used directly; instead, developers choose between `RefCounted` for autonomous resources or `Node` for scene-graph entities.

## RefCounted: Automatic Memory Management

The `RefCounted` branch implements Godot’s reference-counting pattern, where the engine maintains an internal counter that increments on `reference()` and decrements on `unreference()` calls. When the count reaches zero, the object frees itself automatically.

### GdRef Wrapper Implementation

In `src/gdext/builtinindex.nim` (lines 108-112), gdext exposes `RefCounted` instances through the `GdRef[T]` type—a distinct wrapper that manages the reference count through Nim’s destructor mechanism. This ensures that when the last `GdRef` goes out of scope, the underlying engine object receives its final `unreference()` call.

### Instantiation and Type Safety

The `instantiate` procedure for `RefCounted` types, defined in `src/gdext/objecttools.nim` (lines 17-23), returns a `GdRef[T]` rather than a raw pointer. This overload leverages the encoding logic in `src/gdext/private/typeshift.nim` (lines 143-155) to safely bridge Nim’s memory model with Godot’s reference counting.

```nim
import gdext

# Returns GdRef[RefCounted] - memory managed automatically

let rc = instantiate RefCounted
echo rc.referenceCount  # Access via GdRef wrapper

```

## Node: Scene Graph Integration

The `Node` branch, defined in `src/gdext/gen/classindex.nim` (lines 35-36) as `ptr object of Object`, represents entities that exist within Godot’s `SceneTree`. Unlike `RefCounted`, `Node` instances do not use reference counting; instead, their lifetime is strictly bound to their position in the tree hierarchy.

### Tree-Based Lifecycle Management

Nodes are owned by the `SceneTree`. When you call `addChild` (bound in `src/gdext/private/native.nim` as `Node_add_child`), the engine assumes ownership. Removal occurs through `queue_free` or `remove_child`, at which point the engine schedules deletion. Because there is no reference counter, storing a raw `Node` pointer after the node leaves the tree results in undefined behavior—hence gdext does not provide a `GdRef` wrapper for `Node` types.

### Instantiation Differences

As implemented in `src/gdext/objecttools.nim`, instantiating a `Node` subclass returns a raw pointer (e.g., `Node2D`), not a `GdRef`. You must immediately attach it to the scene tree to ensure it remains valid.

```nim
import gdext

# Returns raw Node2D pointer - NOT reference counted

let myNode = instantiate(Node2D, "Hero")
self.addChild(myNode)  # Must add to tree to prevent garbage collection

# Later removal

myNode.queueFree()

```

## Key Differences at a Glance

| Feature | **RefCounted** | **Node** |
|---------|---------------|----------|
| **Base inheritance** | `Object` → `RefCounted` | `Object` → `Node` |
| **Memory management** | Automatic reference counting via `GdRef[T]` | Manual scene-tree ownership |
| **Wrapper type** | `GdRef[T]` (from `builtinindex.nim`) | Raw pointer (no wrapper) |
| **Instantiation** | `instantiate RefCounted` → `GdRef[T]` | `instantiate(NodeType, "Name")` → raw pointer |
| **Lifecycle control** | `reference()` / `unreference()` (automatic) | `add_child` / `queue_free` (manual) |
| **Typical usage** | Resources, data containers, utilities | Scene entities, UI elements, physics bodies |

## Implementation Details

The distinction between these types is enforced at the binding level across several core files:

- **`src/gdext/builtinindex.nim`**: Defines the `GdRef` wrapper and `RefCounted` base methods. The `GdRef[T]` type ensures that Nim’s destructor pattern invokes Godot’s `unreference()` when the wrapper goes out of scope.

- **`src/gdext/private/typeshift.nim`**: Contains the marshalling logic (lines 143-155) that converts between Nim’s `GdRef` and Godot’s raw `RefCounted` pointers during API calls.

- **`src/gdext/private/native.nim`**: Houses the low-level method bindings for both branches, including `RefCounted_reference`, `RefCounted_unreference`, and `Node_add_child`, `Node_queue_free`.

- **`src/gdext/objecttools.nim`**: Provides the `instantiate` overloads that return the appropriate type—`GdRef[T]` for `RefCounted` descendants and raw pointers for `Node` descendants.

- **`src/gdext/sugars.nim`**: Offers the `as` and `castTo` helpers for converting between node types without the `GdRef` wrapper overhead.

## Summary

- **Object** is the abstract root for all engine types in gdext, but you typically work with its two concrete branches.
- **RefCounted** provides automatic memory management through the `GdRef[T]` wrapper, making it ideal for resources and data objects that exist outside the scene tree.
- **Node** requires manual lifecycle management via the scene tree (`add_child`/`queue_free`) and returns raw pointers on instantiation, reflecting its tight integration with Godot’s scene graph.
- Choose **RefCounted** when you need safe, copyable references to engine objects; choose **Node** when building scene hierarchies, UI, or gameplay entities.

## Frequently Asked Questions

### When should I use RefCounted instead of Node?

Use **RefCounted** for objects that function as data containers or utilities independent of the scene graph—such as custom resource classes, mathematical helpers, or configuration objects. Use **Node** exclusively for entities that must participate in the scene tree, including visible objects, physics bodies, UI controls, and anything requiring `_process` or `_physics_process` callbacks.

### Can I convert a RefCounted to a Node or vice versa?

No, these represent distinct inheritance branches under `Object`. While you can cast between sibling types within the same branch (e.g., `Node2D` to `Node` using `as` from `sugars.nim`), you cannot cast a `RefCounted` to a `Node` because they share only the `Object` base and have incompatible memory management semantics.

### How does gdext handle memory safety for Nodes?

gdext does not provide a `GdRef` wrapper for `Node` types because Nodes are owned by the `SceneTree`, not by reference counts. To prevent use-after-free errors, you must ensure that you do not hold raw `Node` pointers after calling `queue_free` or `remove_child`. The binding relies on the developer to respect the scene tree lifecycle, as enforced by Godot’s engine architecture.

### What happens if I forget to add a Node to the scene tree?

If you instantiate a `Node` subclass using `instantiate(NodeType, "Name")` but never call `add_child`, the node remains detached from the `SceneTree`. Since Nodes lack reference counting, the Nim garbage collector may eventually collect the raw pointer if no