# Chapter 17: `unique` ownership

Reference counting lets any number of bindings share a class instance. That flexibility has two costs: a count in every object and an atomic increment or decrement whenever a reference is copied or dropped. For values that only ever have one owner — a connection, a file, a buffer being filled, a job moving through a queue — `unique` removes both. A `unique` value has exactly one owner at a time, the compiler tracks every move, and a native binary keeps no reference count for it.

This chapter covers `unique let` and `unique var` bindings, `unique class`, how `unique` values move through functions, loops, collections and closures, `consume` for handing a value over to ARC, and move-only structs. The normative text is the Language Reference §3-2.4.7 and §1-8.11.

Where `unique` is available:

| Feature | Modes |
|---|---|
| `unique let` / `unique var` bindings, `unique var` struct fields | every mode |
| `unique class` | `host.native` and `freestanding` (`E_UNIQUE_CLASS_ARC_BINDING` elsewhere, chapter 2) |
| `consume` (downgrade to ARC) | `host.native` and `host.dual` (`E_CONSUME_NOT_AVAILABLE` elsewhere) |

## `unique let` and moves

`unique let` binds a class instance to a single owner. Assigning it to another binding *moves* it: the new binding owns the instance and the old one can no longer be used.

```loup
@mode(host.native)

class Connection {
    pub let addr: string;
    new(addr: string) { this.addr = addr; }
}

func describe(c: Connection): string {
    return i"connection to {$ c.addr $}";
}

func main(): void {
    unique let primary = Connection("db1:5432");
    println(describe(primary));     # a reads borrow: primary is still ours
    unique let current = primary;   # move
    println(describe(current));
}
```

*Listing 17-1: A `unique let` binding, borrowed and then moved*

```text
connection to db1:5432
connection to db1:5432
```

Passing `primary` to `describe` does not move it: a parameter without a verb is a `reads` borrow (chapter 15), and the caller keeps ownership. Only an assignment, a `takes` argument, an insertion into a collection, or a closure capture moves a `unique` value.

### Mistake: using a value after moving it

```loup,does_not_compile
@mode(host.native)

class Connection {
    pub let addr: string;
    new(addr: string) { this.addr = addr; }
}

func main(): void {
    unique let primary = Connection("db1:5432");
    let current = primary;
    println(current.addr);
    println(primary.addr);
}
```

```text
main.lphc:12:13: error[E_USE_AFTER_MOVE]: E_USE_AFTER_MOVE: binding 'primary' was moved and cannot be used again — `unique class` instances and move-only structs have single ownership
```

Note that `current` is a plain `let`, and the assignment still moves: a binding initialized from a `unique` binding is itself `unique` (ruling UNQ-2). The only way to get an ordinary shared reference out of a `unique` value is the explicit `consume`, covered below. After the move, use `current`.

`unique var` is the reassignable form. Assigning a fresh value replaces the old one, which a native binary frees on the spot:

```loup
#~ @mode(host.native)
#~
#~ class Connection {
#~     pub let addr: string;
#~     new(addr: string) { this.addr = addr; }
#~ }
#~
func main(): void {
    unique var conn = Connection("db1:5432");
    println(conn.addr);
    conn = Connection("db2:5432");    # the first connection is released here
    println(conn.addr);
}
```

*Listing 17-2: Replacing the value of a `unique var`*

```text
db1:5432
db2:5432
```

> **Note.** A `unique let` binding of an ordinary class is still treated as an ARC receiver when you call a `modifier` method on it, so the call needs a `safe { }` block or a `safe func` (chapter 15). A `unique class` instance, below, does not.

## `unique class`

A `unique class` can only exist in single-owner form. Every binding of one is unique, whether or not it says so, and there is no reference-counted version of it. Use it for types that must never be shared: file handles, locks, device mappings, builders that are filled and then handed off.

```loup
@mode(host.native)

unique class Batch {
    var items: list[int] = [];

    pub modifier func add(x: int): void {
        this.items.push(x);
    }

    pub func total(): int {
        var sum = 0;
        for (x in this.items) {
            sum = sum + x;
        }
        return sum;
    }

    pub func count(): int {
        return this.items.length();
    }
}

func fill(edits b: Batch, n: int): void {
    for (i in 1..n + 1) {
        b.add(i * 10);
    }
}

func report(b: Batch): void {
    println(i"{$ b.count() $} items, total {$ b.total() $}");
}

func submit(takes b: Batch): int {
    return b.total();
}

func main(): void {
    let batch = Batch();
    batch.add(5);
    fill(edits: batch, 3);
    report(batch);
    let sent = submit(takes: batch);
    println(i"submitted {$ sent $}");
}
```

*Listing 17-3: A `unique class` passed by `reads`, `edits` and `takes`*

```text
4 items, total 65
submitted 65
```

Three things differ from an ordinary class:

- `batch.add(5)` needs no `safe { }` block. The compiler knows `batch` is the only reference, so nothing else can be changing the object at the same time.
- `fill` changes the batch through `edits` without being a `safe func`.
- `submit(takes: batch)` is a real move. In a native binary nothing is counted; ownership passes to `submit`, which frees the batch when it returns.

### Mistake: using a value after `takes`

```loup,does_not_compile
@mode(host.native)

unique class Batch {
    pub var size: int = 0;
}

func submit(takes b: Batch): int {
    return b.size;
}

func main(): void {
    let batch = Batch();
    let sent = submit(takes: batch);
    println(i"{$ sent $} sent, {$ batch.size $} left");
}
```

```text
main.lphc:14:35: error[E_USE_AFTER_TAKE]: E_USE_AFTER_TAKE: binding 'batch' was consumed by a `takes` parameter and cannot be used again
```

Read what you need before the call (`let left = batch.size;`). Since Loup 1.24 only `takes` consumes; `reads`, `edits` and `shares` arguments leave the binding usable.

### Moves inside loops

A move inside a loop body happens on every iteration, so the second iteration would use a value the first one already gave away:

```loup,does_not_compile
@mode(host.native)

unique class Job {
    pub let id: int;
    new(id: int) { this.id = id; }
}

func run(takes j: Job): void {
    println(i"running job {$ j.id $}");
}

func main(): void {
    let job = Job(1);
    for (round in 0..3) {
        run(takes: job);
    }
}
```

```text
main.lphc:15:20: error[E_USE_AFTER_TAKE]: E_USE_AFTER_TAKE: binding 'job' was consumed by a `takes` parameter in a previous iteration of the loop and cannot be used again
```

The message says which iteration is the problem (ruling LOOPMV-1). Either create the value inside the loop, or give a `var` a fresh value on every path back to the top of the loop:

```loup
#~ @mode(host.native)
#~
#~ unique class Job {
#~     pub let id: int;
#~     new(id: int) { this.id = id; }
#~ }
#~
#~ func run(takes j: Job): void {
#~     println(i"running job {$ j.id $}");
#~ }
#~
func main(): void {
    var job = Job(1);
    for (next in 2..4) {
        run(takes: job);
        job = Job(next);     # re-initialized before the next iteration
    }
    run(takes: job);
}
```

*Listing 17-4: Re-initializing a moved `var` inside the loop*

```text
running job 1
running job 2
running job 3
```

### `del` and `finalize`

`del` ends a `unique` value's life at once: it runs the class's `finalize()` method and frees the instance, and the binding cannot be used afterwards.

```loup
#~ @mode(host.native)
unique class TempFile {
    pub let path: string;

    new(path: string) {
        this.path = path;
        println(i"create {$ path $}");
    }

    func finalize(): void {
        println(i"remove {$ this.path $}");
    }
}

func main(): void {
    let scratch = TempFile("/tmp/build-42");
    println(i"writing to {$ scratch.path $}");
    del scratch;
    println("done");
}
```

*Listing 17-5: `del` runs `finalize()` immediately*

```text
create /tmp/build-42
writing to /tmp/build-42
remove /tmp/build-42
done
```

Using the binding after `del` is `E_USE_AFTER_DEL: use of binding 'scratch' after `del` — the binding is uninitialized once it has been passed to `del` (§1-6.11)`.

> **Note.** In a native binary, a `unique` value that goes out of scope without `del` is finalized and freed at the end of its scope. `loup run` does not do this: the interpreter calls `finalize()` only for an explicit `del`. Use `del` where cleanup must happen at a known point on every delivery path. Chapter 9 covers `finalize()` and `del` for ordinary classes.

## `consume`: handing a value over to ARC

Sometimes a value starts with one owner and later needs to be shared — a connection that is configured exclusively and then placed in a pool. `consume` converts a `unique` binding into an ordinary reference-counted reference. It is one-way: after it, the compiler no longer tracks uniqueness.

```loup
#~ @mode(host.native)
class Connection {
    pub let addr: string;
    pub var retries: int = 0;
    new(addr: string) { this.addr = addr; }
}

safe func configure(edits c: Connection): void {
    c.retries = 3;
}

func main(): void {
    unique let conn = Connection("db1:5432");
    configure(edits: conn);                   # exclusive setup
    let pooled: Connection = consume conn;    # now an ordinary ARC reference
    let alias = pooled;                       # sharing is allowed again
    println(i"{$ alias.addr $} retries={$ pooled.retries $}");
}
```

*Listing 17-6: Configuring a value exclusively, then sharing it*

```text
db1:5432 retries=3
```

### Mistake: a reserved word as the binding name

The specification's own `consume` example names its result `shared`. That does not compile, because `shared` is a reserved word (it declares `shared class`, chapter 20):

```loup,does_not_compile
@mode(host.native)

class Connection {
    pub let addr: string;
    new(addr: string) { this.addr = addr; }
}

func main(): void {
    unique let conn = Connection("db1:5432");
    let shared: Connection = consume conn;
}
```

```text
main.lphc:10:9: error[E0001]: `shared` is a reserved word (§1-3.4) and cannot name a binding
  note: rename the binding — e.g. `shared_value`
```

`local` and `template` are reserved for the same kind of reason. Pick another name, as Listing 17-6 does.

### Mistakes with `consume`

`consume` only applies to a `unique` binding of an ordinary class:

```loup,does_not_compile
@mode(host.native)

class Connection {
    pub let addr: string;
    new(addr: string) { this.addr = addr; }
}

func main(): void {
    let conn = Connection("db1:5432");
    let pooled: Connection = consume conn;
}
```

```text
main.lphc:10:30: error[E_CONSUME_NON_UNIQUE]: E_CONSUME_NON_UNIQUE: 'consume' can only be applied to 'unique' bindings, but 'conn' is not unique
```

`conn` is already an ordinary reference, so there is nothing to convert: drop the `consume`. The opposite case, a `unique class`, cannot be converted at all, because no reference-counted form of the class exists:

```loup,does_not_compile
@mode(host.native)

unique class Lock {
    pub let name: string;
    new(name: string) { this.name = name; }
}

func main(): void {
    let lock = Lock("db");
    let pooled = consume lock;
}
```

```text
main.lphc:10:18: error[E_UNIQUE_CLASS_CONSUME]: E_UNIQUE_CLASS_CONSUME: `consume` on unique class binding 'Lock' is not allowed — unique class instances cannot be downgraded to ARC
main.lphc:10:18: error[E_CONSUME_NON_UNIQUE]: E_CONSUME_NON_UNIQUE: 'consume' can only be applied to 'unique' bindings, but 'lock' is not unique
```

The first message is the real one. If a type must sometimes be shared, make it an ordinary class and put `unique` on the bindings that need it.

> **Known gap.** The specification says that using a binding after `consume` is `E_USE_AFTER_MOVE`. Loup 1.25.1 does not report it: `println(conn.addr)` after `consume conn` compiles. Treat `consume` as the binding's last use.

## Move-only structs

A struct field may be declared `unique var`. The struct then owns that value, and the whole struct becomes *move-only*: assigning it moves it instead of copying it, because a copy would give the `unique` value two owners.

```loup
@mode(host.native)

class Handle {
    pub let fd: int;
    new(fd: int) { this.fd = fd; }
}

struct Resource {
    name: string;
    unique var handle: Handle;
}

func reopen(edits r: Resource, takes h: Handle): void {
    r.handle = h;           # the old handle is released
}

func close(takes r: Resource): int {
    return r.handle.fd;
}

func main(): void {
    var res = Resource(name = "db", handle = Handle(3));
    let fresh = Handle(7);
    reopen(edits: res, takes: fresh);
    println(i"{$ res.name $} -> fd {$ res.handle.fd $}");
    println(i"closed fd {$ close(takes: res) $}");
}
```

*Listing 17-7: A move-only struct with a `unique var` field*

```text
db -> fd 7
closed fd 7
```

### Mistake: copying a move-only struct

```loup,does_not_compile
@mode(host.native)

class Handle {
    pub let fd: int;
    new(fd: int) { this.fd = fd; }
}

struct Resource {
    name: string;
    unique var handle: Handle;
}

func main(): void {
    var primary = Resource(name = "db", handle = Handle(3));
    var backup = primary;
    println(primary.name);
}
```

```text
main.lphc:16:13: error[E_USE_AFTER_MOVE]: E_USE_AFTER_MOVE: binding 'primary' was moved and cannot be used again — `unique class` instances and move-only structs have single ownership
```

An ordinary struct would have been copied. This one moved, because it holds a `unique` field. Build a second resource with its own handle if you need two.

### Mistake: moving a field out of a borrowed struct

A function that receives a move-only struct as a borrow may read its fields but not move them out:

```loup,does_not_compile
@mode(host.native)

unique class Socket {
    pub let port: int;
    new(port: int) { this.port = port; }
}

struct Pending {
    name: string;
    unique var socket: Socket;
}

func accept(p: Pending): Socket {
    println(i"accepting {$ p.name $}");
    return p.socket;
}

func main(): void {
    let p = Pending(name = "web", socket = Socket(8080));
    let s = accept(p);
    println(s.port);
}
```

```text
main.lphc:15:12: error[E_MOVE_OUT_OF_BORROW]: E_MOVE_OUT_OF_BORROW: cannot move `p.socket` out of the borrowed parameter `p` — only a `takes` parameter owns its value (§1-8.11); declare it `takes p`
```

`accept` hands the socket to its caller, so it must own the struct (ruling MOVEB-1). Declare `func accept(takes p: Pending): Socket` and call it as `accept(takes: p)`; the program then prints `accepting web` and `8080`.

The field rules, all checked:

- a `unique` field must be `unique var` (`E_UNIQUE_FIELD_LET` for `unique let`);
- `unique` applies only to class types (`E_UNIQUE_NOT_CLASS: 'unique' can only be applied to class types, not 'int'`);
- an ordinary class may not have a `unique` field, because the class itself can be shared (`E_UNIQUE_CLASS_FIELD`); a `unique class` may.

> ⚠ **Planned.** `consume r.handle`, downgrading one field of a move-only struct while keeping the rest, is described in earlier editions but not implemented: Loup 1.25.1 reports `E_CONSUME_NON_UNIQUE` for it.

## Collections of `unique` values

A `list` can hold `unique` values (ruling UNIQCOLL-1). The collection becomes the owner, and the compiler tracks what goes in and comes out:

- inserting (`push`, an element assignment, a list literal) moves the value in;
- `pop` and `remove` move a value out;
- indexing, `get`, `first`, `last` and a `for` loop variable *borrow* an element in place.

```loup
@mode(host.native)

unique class Job {
    pub let id: int;
    new(id: int) { this.id = id; }
}

func main(): void {
    var queue: list[Job] = [];
    queue.push(Job(1));
    queue.push(Job(2));
    queue.push(Job(3));
    for (j in queue) {
        println(i"queued {$ j.id $}");
    }
    println(i"next up: {$ queue[0].id $}");
    let last = queue.pop();
    println(i"took job {$ last.id $}, {$ queue.length() $} left");
}
```

*Listing 17-8: A queue that owns its jobs*

```text
queued 1
queued 2
queued 3
next up: 1
took job 3, 2 left
```

Taking an element out by indexing, or copying the collection, would leave two owners, so all of these are refused:

```loup,does_not_compile
@mode(host.native)

unique class Job {
    pub let id: int;
    new(id: int) { this.id = id; }
}

func main(): void {
    var queue: list[Job] = [Job(1), Job(2)];
    let first = queue[0];
    let copy = queue.clone();
    let head = queue[0:1];
}
```

```text
main.lphc:10:17: error[E_MOVE_OUT_OF_BORROW]: E_MOVE_OUT_OF_BORROW: cannot move `queue[…]` out of the collection `queue` — an element accessed in place is a borrow (§1-8.11); move it out with `remove` or `pop`
main.lphc:11:16: error[E_MOVE_OUT_OF_BORROW]: E_MOVE_OUT_OF_BORROW: cannot move the elements of `queue` out through `clone()` — a collection holding a `unique` value is move-only (§1-8.11); move elements out with `remove` or `pop`, iterate with `for` (a borrow)
main.lphc:12:16: error[E_MOVE_OUT_OF_BORROW]: E_MOVE_OUT_OF_BORROW: cannot move the elements of `queue` out through a slice — a collection holding a `unique` value is move-only (§1-8.11); move elements out with `remove` or `pop`, iterate with `for` (a borrow)
```

Each message names the replacement. Use `queue.remove(0)` to take the first job out, and read fields in place (`queue[0].id`) when you only need to look. Inserting a value is a move too: after `queue.push(job)`, using `job` is `E_USE_AFTER_TAKE`. A collection holding `unique` values is itself move-only, so `let other = queue;` moves the whole queue.

## Closures capture by moving

A closure that uses a `unique` value captures it by moving it in (ruling CAPMV-1). The closure body only borrows it, so the closure can be called many times, but the enclosing function cannot use the value afterwards:

```loup,does_not_compile
@mode(host.native)

unique class Logger {
    pub let name: string;
    new(name: string) { this.name = name; }
    pub func log(msg: string): void { println(i"[{$ this.name $}] {$ msg $}"); }
}

func main(): void {
    let logger = Logger("app");
    let report = func(msg: string): void {
        logger.log(msg);
    };
    report("started");
    logger.log("finished");
}
```

```text
main.lphc:15:5: error[E_USE_AFTER_MOVE]: E_USE_AFTER_MOVE: binding 'logger' was moved and cannot be used again — `unique class` instances and move-only structs have single ownership
```

The capture is a move even though the closure never leaves `main`, and even when the closure is passed straight to a function. Route every use through the closure, or keep the closure's work separate from the value's other uses:

```loup
#~ @mode(host.native)
#~
#~ unique class Logger {
#~     pub let name: string;
#~     new(name: string) { this.name = name; }
#~     pub func log(msg: string): void { println(i"[{$ this.name $}] {$ msg $}"); }
#~ }
#~
func main(): void {
    let logger = Logger("app");
    let report = func(msg: string): void {
        logger.log(msg);
    };
    report("started");
    report("finished");
}
```

*Listing 17-9: After the capture, the closure owns the logger*

```text
[app] started
[app] finished
```

A closure may not capture a `unique` value that the function only borrows: capturing parameter `l` of `func useIt(l: Logger)` is `E_MOVE_OUT_OF_BORROW`, with the same advice to declare it `takes l`.

Moving a `unique` value into a thread works the same way, with one more rule: the captured type must be `Sendable` (chapter 20). A `unique class` that declares `implements Sendable` can be moved into a `Thread.spawn` closure, and using it afterwards is `E_USE_AFTER_MOVE`.

## When to use `unique`

Reach for `unique` when a value has one owner at every moment and you want the compiler to prove it: resources with a `finalize`, buffers being built, jobs moving through a pipeline. Keep ordinary classes for data that many parts of the program read, for object graphs, and for anything you store in several places. Mixing is normal: `unique` while a value is being set up or handed along, `consume` at the point where it becomes shared.

## Error codes

| Code | Meaning |
|---|---|
| `E_USE_AFTER_MOVE` | a binding used after it was moved by assignment or closure capture |
| `E_USE_AFTER_TAKE` | a binding used after a `takes` argument or an insertion into a collection, including "in a previous iteration of the loop" |
| `E_USE_AFTER_DEL` | a binding used after `del` |
| `E_MOVE_OUT_OF_BORROW` | moving a `unique` value out of a borrowed parameter, a collection element or a slice/clone |
| `E_CONSUME_NON_UNIQUE` | `consume` of a binding that is not `unique` |
| `E_UNIQUE_CLASS_CONSUME` | `consume` of a `unique class` instance |
| `E_CONSUME_NOT_AVAILABLE` | `consume` outside `host.native` and `host.dual` |
| `E_UNIQUE_CLASS_ARC_BINDING` | `unique class` outside `host.native` and `freestanding` |
| `E_UNIQUE_NOT_CLASS` | `unique` on a non-class type |
| `E_UNIQUE_FIELD_LET` | a `unique let` field |
| `E_UNIQUE_CLASS_FIELD` | a `unique` field in a class that is not a `unique class` |

`E_UNIQUE_NOT_NATIVE`, which older editions listed, was removed in v1.17: `unique` bindings work in every mode.

## Summary

- `unique let` / `unique var` give a class instance a single owner; assignment, `takes`, insertion into a collection and closure capture move it, and a later use is an error.
- A `unique class` has no shared form; its `modifier` methods need no `safe` context, and it is available in `host.native` and `freestanding`.
- Moves inside loops, moves out of borrowed parameters and moves out of collection elements are all caught (`E_USE_AFTER_TAKE` "in a previous iteration", `E_MOVE_OUT_OF_BORROW`).
- `consume` converts a `unique` binding of an ordinary class into an ARC reference, one way; using the binding afterwards is not yet reported.
- A struct with a `unique var` field is move-only; collections of `unique` values are move-only too.
- `del` runs `finalize()` at once; under `loup run` that is the only time `finalize()` runs.
- Chapter 18 introduces lifetimes, which let borrows outlive a single call.
