> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/scripting/arrays-and-collections.md).

# Advanced Language Reference

CSL gives you a few “array-like” tools that cover most gameplay needs: fixed arrays, slices, and dynamic arrays. This page is the practical guide to using them without foot-guns.

## Quick glossary

* **Fixed array**: `[N]T` — size is known at compile time.
* **Slice / managed array**: `[]T` — a view over array data (often used for parameters).
* **Dynamic array**: `[..]T` — resizable list with `count` and `capacity`.

```go
fixed: [4]int = {1, 2, 3, 4};
view: []int = fixed; // slice view

list: [..]int = {10, 20};
```

{% hint style="info" %}
Dynamic array methods use normal dot calls (e.g. `list.append(x)`). Fields also use dot access (e.g. `list.count`).
{% endhint %}

## Dynamic arrays (`[..]T`)

Dynamic arrays are the “default list” type for gameplay code.

### Creating and appending

```go
players_seen: [..]string = {"Ada", "Grace"};
players_seen.append(player.get_user_id());
```

Dynamic-array literals may contain runtime expressions and can be returned directly when the expected type is `[..]T`:

```go
make_values :: proc(seed: int) -> [..]int {
    return {seed, seed + 1};
}
```

Dynamic-array class field defaults are also supported. Each class instance receives independent mutable storage:

```go
Loadout :: class {
    slots: [..]string = {"sword", "potion"};
}
```

### Reserving capacity (performance)

If you know you’ll add a lot of items, reserve first to avoid repeated reallocations.

```go
results: [..]Enemy;
results.reserve(128);
```

### Removing items

You generally pick between **fast removal** (order doesn’t matter) and **ordered removal** (preserve order).

```go
values: [..]int;
values.append(10);
values.append(20);
values.append(30);

values.unordered_remove_by_value(20); // swaps with last, fast
values.ordered_remove_by_value(10);   // shifts, keeps order
```

You can also remove by index:

```go
values.unordered_remove_by_index(0);
values.ordered_remove_by_index(0);
```

### Clearing

```go
values.clear(); // O(1): just sets count to 0
```

## Passing arrays into procedures

Many APIs accept `[]T` (a slice/view). Dynamic arrays can be passed where `[]T` is expected.

```go
sum :: proc(arr: []int) -> int {
    total := 0;
    for v: arr {
        total += v;
    }
    return total;
}

nums: [..]int;
nums.append(1);
nums.append(2);
nums.append(3);

total := sum(nums); // implicit [..]int -> []int
```

## Iteration patterns

### Iterate elements

```go
for v: nums {
    log_info("v=%", {v});
}
```

### Iterate indices

Use `..<` when you want a normal "0 up to, but not including, count" loop:

```go
for i: 0..<nums.count {
    log_info("nums[%]=%", {i, nums[i]});
}
```

Some engine collections provide iterators. `inventory.slots()` visits every slot in order, yields `null` for empty slots, and exposes the actual slot as the optional index:

```go
for item, slot: inventory.slots() if item != null {
    log_info("item % is in slot %", {item.get_definition().id, slot});
}
```

{% hint style="warning" %}
`..` ranges are inclusive. `..<` ranges exclude the upper bound.
{% endhint %}

Reverse loops use `#reverse`:

```go
for i: 0..<nums.count #reverse {
    log_info("nums[%]=%", {i, nums[i]});
}
```

## Hashtables

Use `Hashtable(Key_Type, Value_Type)` when you need fast lookup by key. It supports integer and string keys, along with the usual methods for lookup, insertion, overwrite, removal, and iteration.

```go
scores: Hashtable(string, s64);
scores.add("alice", 10);
scores.add_or_overwrite("bob", 25);

score, ok := scores.find("alice");
if ok {
    log_info("alice score: %", {score});
}

for value, key: scores {
    log_info("% = %", {key, value});
}
```

Use `add` when duplicate keys should assert. Use `add_or_overwrite` when updating an existing key is expected. `try_add` returns `true` if the key already existed and leaves the old value unchanged.

## Closures and callbacks

Procedure literals may capture surrounding values, but capturing procedures are callback expressions and cannot be stored in collections or other locations. A procedure parameter must be marked `#no_captures` before it can be stored, and callers may then pass only non-capturing procedures; see [Closures and callbacks](/scripting/syntax.md#closures-and-callbacks) for the usage rules and explicit-userdata pattern.

## API reference

Each project includes an `api_references/` folder containing the current core API. Open the matching `.csl_engine` file to inspect a type or procedure.
