# Ownership and reused buffers

> How Alice proves a local array is the only reference to its memory, recycles it without the GC, and how own turns that into a compile-time contract.

Canonical: https://alice-lang.org/documentation/advanced/ownership/

[Documentation index](https://alice-lang.org/documentation/llms.txt) · [Complete book](https://alice-lang.org/llms-full.txt)

Alice runs on the .NET garbage collector and keeps it as the safety net. On top of it, the compiler tracks **ownership** of local arrays: when it can prove a local is the only reference to its array, it knows the exact point where the program drops that array, and hands the memory back to a per-thread pool instead of leaving it to the collector. The next allocation of the same length reuses it. A loop that builds a fresh row per iteration stops allocating once warm.

Ownership is available from Alice 0.12.

## What is owned

A local array of unmanaged elements (`Int32`, `Float64`, `Bool`, and similar) is owned when every value it receives is a fresh `Array(T).new(n)` or `Array(T).new(n, v)`, or another owned local, and every use is one of these:

| Use                                                 | Example                    |
| --------------------------------------------------- | -------------------------- |
| Read or write an element                            | `row[j] = row[j - 1] + 1`  |
| Read the size                                       | `row.size`, `row.length`   |
| Iterate                                             | `for v in row`             |
| Lend it to an Alice parameter that does not keep it | `fill(row, 0.0)`           |
| Move it to another owned local                      | `previous = row`           |
| Return it                                           | `row` as the method result |

A method parameter only *borrows* an array when the callee does the same: indexes, sizes, iterates, or lends it on. A callee that stores, returns, or captures it takes the array out of ownership at the call site.

Storing an array in a field or collection, capturing it in a block or lambda, passing it to a .NET method, or using it inside `begin`/`rescue` leaves it an ordinary GC array. Nothing changes for your program except that it allocates.

## Moves

Assigning one owned local to another moves the array:

```alice
def edit_distance(a : String, b : String) : Int32
  previous = Array(Int32).new(b.size + 1)
  for j in 0..b.size
    previous[j] = j
  end
  for i in 1..a.size
    row = Array(Int32).new(b.size + 1)    # reuses the buffer released below
    row[0] = i
    for j in 1..b.size
      cost = a[i - 1] == b[j - 1] ? 0 : 1
      row[j] = [row[j - 1] + 1, previous[j] + 1, previous[j - 1] + cost].min
    end
    previous = row                        # move: the old previous goes back to the pool
  end
  previous[b.size]
end
```

After `previous = row`, `row` is moved: reading it before assigning it a new array would observe an array someone else now owns. The compiler follows moves through branches and loops; it releases an array only where it is *definitely* live and *definitely* not shared. This loop allocates two rows in total instead of one per row.

## The `own` contract

Inference is silent: a local that breaks a rule simply stays a GC array. Write `own` to make the rules a contract the compiler checks:

```alice
own scratch = Array(Float64).new(1024)
```

An escape becomes a compile error:

```text
error[AL0207]: owned value `scratch` escapes: `keep` may keep a reference to it
```

So does reading a moved local:

```alice
own a = Array(Int32).new(n)
own b = a
a[0]        # compile error AL0208: use after move
```

Reassign the local (`a = Array(Int32).new(n)`) before using it again, or read through the new owner.

## What happens at runtime

Owned allocations come from `Own(T)` in the Alice runtime: four slots per element type per thread, matched by exact length so `.size` never changes. A reused array is zeroed, or filled with `v` for `Array(T).new(n, v)`, exactly like a new one. Arrays larger than 64 MiB are left to the GC rather than retained by an idle thread.

Releases happen before an owned local is overwritten and at every exit of the method, except for the array being returned. Nothing is released when an exception unwinds: the array becomes ordinary garbage. A missed release costs one allocation, never memory safety, and there is no `try/finally` in your hot method.

## Limits

- Only local arrays of unmanaged elements are owned. Fields, collections of references, and objects stay with the GC.
- Buffers are reused by the thread that released them.
- Arrays shared with another thread, a block, or .NET code are never owned.

See [memory and allocation](https://alice-lang.org/documentation/advanced/memory.md) for the other ways Alice keeps hot code off the heap, and [performance](https://alice-lang.org/documentation/guides/max-performance.md) for how ownership fits with the rest of the optimizer.

## Continue reading

- Previous: [Write secure Alice applications](https://alice-lang.org/documentation/advanced/security.md)
- Next: [Editor setup](https://alice-lang.org/documentation/tools/editors.md)
