# Chapter 8: Classes and Objects

**What you'll learn:** how to declare classes with fields, a constructor and methods; why methods that change an object are marked `modifier` and called inside `safe`; constructors, static factories and visibility; inheritance with `super` and `override`; abstract classes; equality; `dynamic class`; and how objects release resources.

Structs (Chapter 6) bundle data and are copied when you assign them. A **class** bundles data and behaviour, and its instances are **objects** that are shared by reference: assigning an object to another variable, or passing it to a function, gives a second name for the same object. Use a class when something has an identity — an account, a session, a parser in the middle of its input — or when you want inheritance.

## Defining a Class

```loup
@mode(host.scripted)

class Dog {
    pub let name: string;
    pub let breed: string;
    pub var age: int;

    new(name: string, breed: string, age: int) {
        this.name = name;
        this.breed = breed;
        this.age = age;
    }

    pub func bark(): string {
        return i"{$ this.name $} the {$ this.breed $} says: Woof!";
    }

    pub modifier func haveBirthday(): void {
        this.age = this.age + 1;
    }
}

let dog = Dog("Rex", "Labrador", 3);
println(dog.bark());
safe {
    dog.haveBirthday();
}
println(i"{$ dog.name $} is now {$ dog.age $}");
println(dog);
```

*Listing 8-1: A class with fields, a constructor and two methods*

```text
Rex the Labrador says: Woof!
Rex is now 4
Dog(name: "Rex", breed: "Labrador", age: 4)
```

The pieces:

- **Fields** are declared with `let` (cannot change after construction) or `var` (can change), followed by a type. `pub` makes a field readable outside the class.
- **`new(…) { … }`** is the constructor. A class has at most one. You create an object by calling the class like a function: `Dog("Rex", "Labrador", 3)`.
- **Methods** are functions declared inside the class. Inside a method or the constructor, `this` is the object the method was called on, and fields are always reached through it: `this.name`.
- **`modifier`** marks a method that assigns a `var` field. Calling a `modifier` method on an object needs a `safe { … }` block (or a `safe func` caller); the section "Methods That Change an Object" explains why.
- `println` of an object prints its class name and fields.

A class with no `: Base` clause extends `Any`, the root of every class (§1-8.3). You will see `class Dog : Any` in older code; it means the same thing.

### Mistake: a field without `let` or `var`

If you come from TypeScript or Swift, you may write fields the way structs declare them:

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

class Point {
    x: int;
    y: int;
}
```

```text
main.lpscr:4:5: error[E0001]: E_BARE_FIELD: class field `x` must be declared `let` or `var`; write `var x: ...` (mutable) or `let x: ...` (immutable)
main.lpscr:5:5: error[E0001]: E_BARE_FIELD: class field `y` must be declared `let` or `var`; write `var y: ...` (mutable) or `let y: ...` (immutable)
```

Struct fields need no keyword; class fields always do (ruling FIELD-1). Choose `let` for a field that is fixed once the object exists, `var` for one that changes, and add `pub` if code outside the class reads it: `pub let x: int;`.

### Mistake: leaving out `this.`

In Java or C#, a method can name a field directly. Loup 1.25 requires `this.`:

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

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

    pub func bark(): string {
        return i"{$ name $} says: Woof!";
    }
}

println(Dog("Rex").bark());
```

```text
main.lpscr:8:21: error[E0001]: undefined identifier `name`
```

The compiler looked for a local variable or parameter called `name` and found none. Write `this.name`. The same goes for calling another method of the same object: `this.bark()`, not `bark()`.

> Some examples in the Language Reference read fields and call methods without `this.` (for instance in §1-8.3 and §1-11.1). The 1.25 compilers do not accept that; always write `this.`.

### Mistake: declaring the receiver as a parameter

Python methods take `self` as their first parameter. In Loup the receiver is implicit, and `this` is a reserved word:

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

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

    pub func greet(this: Dog): string {
        return i"{$ this.name $} says hi";
    }
}
```

```text
main.lpscr:7:20: error[E0001]: `this` is a reserved word (§1-3.4) and cannot name a parameter
  note: rename the parameter — e.g. `this_value`
```

Delete the parameter: `pub func greet(): string { … }`. `this` is available in every method without declaring it.

## Objects Are Shared by Reference

A variable of a class type holds a *reference* to an object. Copying the variable copies the reference:

```text
  Struct (value):                    Class (reference):

  let first = Reading(…)             let savings = Account("Ada")
  +---------------------+            +---------+     +----------------+
  | first  celsius=21.5 |            | savings |---->| owner: "Ada"   |
  +---------------------+            +---------+     | balance: 10100 |
                                                     +----------------+
  var second = first     (copy)      let sameAccount = savings   (same object)
  +---------------------+            +-------------+        ^
  | second celsius=21.5 |            | sameAccount |--------+
  +---------------------+            +-------------+
```

A struct copy is independent of the original:

```loup
#~ @mode(host.scripted)
struct Reading {
    sensor: string;
    var celsius: float;
}

let first = Reading(sensor = "attic", celsius = 21.5);
var second = first;
second.celsius = 30.0;
println(first.celsius);
println(second.celsius);
```

```text
21.5
30
```

An object passed to a function is the caller's object, so a change made by the function is visible to the caller:

```loup
#~ @mode(host.scripted)
class Account {
    pub let owner: string;
    pub var balance: int = 0;

    new(owner: string) {
        this.owner = owner;
    }

    pub modifier func deposit(cents: int): void {
        this.balance = this.balance + cents;
    }
}

safe func payInterest(edits acct: Account): void {
    acct.deposit(acct.balance // 100);
}

let savings = Account("Ada");
safe {
    savings.deposit(10000);
    payInterest(edits: savings);
}
println(savings.balance);

let sameAccount = savings;
println(sameAccount == savings);
println(Account("Ada") == Account("Ada"));
```

*Listing 8-2: A function changes the caller's object; `==` compares identity*

```text
10100
true
false
```

By default `==` on objects asks "is this the same object?", so two separately created accounts with the same contents are not equal. "Equality" below shows how to define your own `==`.

## Methods That Change an Object: `modifier` and `safe`

Because many references can point at one object, Loup makes mutation of an object visible in the source code at three places (§3-2.4.11, §3-2.4.12):

1. A method that assigns a `var` field is declared **`modifier`**.
2. A call to a `modifier` method — or a direct assignment to a `pub var` field from outside the class — happens inside a **`safe { … }`** block or inside a **`safe func`**.
3. A function that changes an object it receives declares the parameter **`edits`**, and the caller writes `edits:` at the call site.

Listing 8-2 shows all three. The rules let a reader find every place an object can change, and they let the compiler check — inside `safe` — that no other reference is being used while the change happens.

### Mistake: forgetting `modifier`

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

class Counter {
    pub var count: int = 0;

    pub func bump(): void {
        this.count = this.count + 1;
    }
}
```

```text
main.lpscr:7:9: error[E_MODIFIER_REQUIRED]: E_MODIFIER_REQUIRED: writing to `var` field 'count' of `self` in method 'bump' of class 'Counter' requires the method to be declared as `modifier`
```

Declare it `pub modifier func bump(): void`. (The message says `self`; it means the object the method was called on, which you write as `this`.)

### Mistake: calling a `modifier` method outside `safe`

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

class Counter {
    pub var count: int = 0;

    pub modifier func bump(): void {
        this.count = this.count + 1;
    }
}

let hits = Counter();
hits.bump();
println(hits.count);
```

```text
main.lpscr:12:1: error[E_SAFE_CONTEXT_REQUIRED]: E_SAFE_CONTEXT_REQUIRED: `modifier` method 'bump' called on ARC receiver of type 'Counter' outside `safe {}` or `safe func` context — wrap the call in `safe { ... }` or mark the enclosing function as `safe func`
```

"ARC receiver" means an ordinary, reference-counted object. Wrap the call, or put the code in a `safe func`:

```loup
#~ @mode(host.scripted)
class Counter {
    pub var count: int = 0;

    pub modifier func bump(): void {
        this.count = this.count + 1;
    }
}

safe func countWords(words: list[string]): Counter {
    let counter = Counter();
    for (word in words) {
        counter.bump();
    }
    return counter;
}

let hits = Counter();
safe {
    hits.bump();
}
println(hits.count);
println(countWords(["to", "be", "or"]).count);
```

```text
1
3
```

Assigning a `pub var` field from outside the class follows the same rule: `hits.count = 0;` on its own is `E_SAFE_CONTEXT_REQUIRED` (the message calls the field a "`modifier` method 'count'"), and `safe { hits.count = 0; }` compiles.

### Mistake: changing an object through a plain parameter

A parameter without a verb is `reads`: the function may look at the object but not change it.

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

class Counter {
    pub var count: int = 0;

    pub modifier func bump(): void {
        this.count = this.count + 1;
    }
}

func record(c: Counter): void {
    safe {
        c.bump();
    }
}
```

```text
main.lpscr:13:9: error[E_READS_MUTATION]: E_READS_MUTATION: parameter 'c' is a `reads` borrow (a parameter without a verb is read-only, §3-2.4.9) and cannot be mutated (`modifier` method `bump` called on it); declare it `edits` and pass `edits: …` to change the caller's value, or copy it into a local first
```

Declare the parameter `edits`, make the function a `safe func`, and acknowledge the verb at the call site, as `payInterest` does in Listing 8-2:

```loup
#~ @mode(host.scripted)
#~ class Counter {
#~     pub var count: int = 0;
#~
#~     pub modifier func bump(): void {
#~         this.count = this.count + 1;
#~     }
#~ }
#~
safe func record(edits c: Counter): void {
    c.bump();
}

let hits = Counter();
safe {
    record(edits: hits);
    record(edits: hits);
}
println(hits.count);
```

```text
2
```

> The Language Reference says a function with an `edits` parameter is implicitly a `safe func` (§3-2.4.12). The 1.25 compiler still reports `E_SAFE_CONTEXT_REQUIRED` inside such a function unless it is declared `safe func`, so write `safe func` explicitly. Chapter 19 covers the verbs in depth.

## Constructors

### Generated constructors

A class that declares no `new` gets one generated from its fields. Arguments are matched to fields **by name**, in any order, and a field with a default value may be left out (ruling IMPCTOR-1):

```loup
#~ @mode(host.scripted)
class RetryPolicy {
    pub let attempts: int;
    pub let delayMs: int = 250;
    pub let backoff: float = 2.0;
}

let standard = RetryPolicy(attempts = 3);
let patient = RetryPolicy(backoff = 1.5, attempts = 10, delayMs = 1000);
println(standard);
println(patient);
```

```text
RetryPolicy(attempts: 3, delayMs: 250, backoff: 2.0)
RetryPolicy(attempts: 10, delayMs: 1000, backoff: 1.5)
```

Leaving out a field that has no default is an error that names the field:

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

class RetryPolicy {
    pub let attempts: int;
    pub let delayMs: int = 250;
}

let policy = RetryPolicy(delayMs = 100);
```

```text
main.lpscr:8:14: error[E0001]: missing required field 'attempts' in 'RetryPolicy' constructor call
```

Write your own `new` when construction needs to do more than copy arguments into fields — validate them, compute a field, or open a resource.

### Static factories instead of overloaded constructors

In Java or C# you might give a class two constructors. A Loup class has at most one `new`:

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

class Endpoint {
    pub let host: string;
    pub let port: int;

    new(host: string, port: int) {
        this.host = host;
        this.port = port;
    }

    new(host: string) {
        this.host = host;
        this.port = 80;
    }
}
```

```text
main.lpscr:12:5: error[E0001]: canonical name collision: 'main.lpscr::Endpoint::constructor' is already assigned to another callable
  note: previously assigned to callable with HirId(5001420)
```

The message comes from deep inside the compiler (the Language Reference names this error `E_DUPLICATE_CONSTRUCTOR`), but the cause is the second `new`. Give the alternatives names as `static` methods — **static factories** (§1-8.4). A factory can also return a `Result` when construction can fail, which `new` cannot:

```loup
#~ @mode(host.scripted)
class Endpoint {
    pub let host: string;
    pub let port: int;

    new(host: string, port: int) {
        this.host = host;
        this.port = port;
    }

    pub static func localhost(port: int = 8080): Endpoint {
        return Endpoint("localhost", port);
    }

    pub static func checked(host: string, port: int): Result[Endpoint, string] {
        if (port < 1 or port > 65535) {
            return Err(i"port {$ port $} is out of range");
        }
        return Ok(Endpoint(host, port));
    }

    pub func address(): string {
        return i"{$ this.host $}:{$ this.port $}";
    }
}

println(Endpoint.localhost().address());
println(Endpoint.localhost(port = 3000).address());

for (port in [443, 70000]) {
    match Endpoint.checked("api.example.com", port) {
        Ok(endpoint) => println(endpoint.address()),
        Err(message) => println(i"error: {$ message $}"),
    }
}
```

*Listing 8-3: Named factories, one of them fallible*

```text
localhost:8080
localhost:3000
api.example.com:443
error: port 70000 is out of range
```

## Visibility

Loup has no `private` or `protected` keyword. Visibility works like this (§1-8.2, ruling FPRIV-1):

| Declaration | Without `pub` | With `pub` |
|---|---|---|
| A class **field** | visible only inside the class and its subclasses | visible everywhere |
| A **method**, function, class or other module-level name | visible in the same file (module) | visible to every module that imports it |

So a field without `pub` is private to its class, even from other code in the same file:

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

class Wallet {
    pub let owner: string;
    var cents: int = 0;

    new(owner: string) {
        this.owner = owner;
    }

    pub modifier func add(amount: int): void {
        this.cents = this.cents + amount;
    }

    pub func balance(): int {
        return this.cents;
    }
}

let wallet = Wallet("Grace");
safe {
    wallet.add(250);
}
println(wallet.cents);
```

```text
main.lpscr:24:9: error[E0001]: member 'cents' of class 'Wallet' is private
```

Read it through the public method instead — `println(wallet.balance());` prints `250`. Keeping `cents` private means every change to it goes through `add`, which is the one place you would add validation later.

> §1-8.2 of the Language Reference still describes all non-`pub` members as module-private. Ruling FPRIV-1 made class fields private to the class, which is what both compilers enforce; methods stay module-private.

## Static Members

A `static` field or method belongs to the class rather than to an object, and is reached through the class name:

```loup
#~ @mode(host.scripted)
class Ticket {
    pub static var issued: int = 0;
    pub let number: int;

    new() {
        Ticket.issued = Ticket.issued + 1;
        this.number = Ticket.issued;
    }

    pub static func clampPriority(value: int): int {
        if (value < 1) {
            return 1;
        }
        if (value > 5) {
            return 5;
        }
        return value;
    }
}

let a = Ticket();
let b = Ticket();
println(i"tickets {$ a.number $} and {$ b.number $}; {$ Ticket.issued $} issued");
println(Ticket.clampPriority(9));
```

```text
tickets 1 and 2; 2 issued
5
```

Static methods have no `this`.

## Inheritance

A class may extend one other class and implement any number of interfaces (Chapter 9). The subclass inherits the parent's fields and methods:

```loup
#~ @mode(host.scripted)
class Animal {
    pub let name: string;

    new(name: string) {
        this.name = name;
    }

    pub func speak(): string {
        return i"{$ this.name $} makes a sound";
    }

    pub func describe(): string {
        return i"I am {$ this.name $}";
    }
}

class Cat : Animal {
    pub let indoor: bool;

    new(name: string, indoor: bool) {
        super(name);
        this.indoor = indoor;
    }

    pub override func speak(): string {
        return i"{$ this.name $} says: Meow!";
    }
}

class Kitten : Cat {
    new(name: string) {
        super(name, true);
    }

    pub override func speak(): string {
        return super.speak() + " (quietly)";
    }
}

let animals: list[Animal] = [Animal("Generic"), Cat("Whiskers", false), Kitten("Tiny")];
for (animal in animals) {
    println(animal.speak());
}
println(Kitten("Bo").describe());
```

*Listing 8-4: Single inheritance, `super(...)`, `override` and `super.method()`*

```text
Generic makes a sound
Whiskers says: Meow!
Tiny says: Meow! (quietly)
I am Bo
```

- `class Cat : Animal` makes `Cat` a subclass of `Animal`. A `Cat` can be used wherever an `Animal` is expected.
- `super(name)` runs the parent's constructor. It must come before the constructor touches `this`.
- `override` marks a method that replaces one the parent already implements (§1-8.3.1).
- `super.speak()` calls the parent's version of the method, so `Kitten` extends `Cat`'s behaviour instead of repeating it.
- The loop calls `speak()` on values typed `Animal`, and each object runs its own class's version. That is dynamic dispatch.

A generated constructor includes the parent's fields, first in the argument list: with `class Animal { pub let name: string; }` and `class Cat : Animal { pub let indoor: bool = true; }`, `Cat(name = "Tom")` works.

### Mistake: overriding without `override`

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

class Animal {
    pub func speak(): string {
        return "...";
    }
}

class Cat : Animal {
    pub func speak(): string {
        return "Meow";
    }
}
```

```text
main.lpscr:10:5: error[E_OVERRIDE_REQUIRED]: method `speak` on class `Cat` shadows a parent method on `Animal` and must be marked `override` (E_OVERRIDE_REQUIRED)
```

The keyword guards against replacing a parent method by accident. Add it: `pub override func speak()`. The reverse mistake — `override` on a method the parent does not have — is `E_OVERRIDE_NO_TARGET`.

### Mistake: forgetting `super(...)`

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

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

class Cat : Animal {
    pub let indoor: bool;

    new(name: string, indoor: bool) {
        this.indoor = indoor;
    }
}
```

```text
main.lpscr:12:9: error[E_SUPER_NOT_CALLED]: E_SUPER_NOT_CALLED: constructor of `Cat` accesses `this` before calling `super(...)`; `Animal` has a non-default constructor (§1-8.4)
```

`Animal`'s constructor needs a name, and only `Cat`'s constructor can supply it. Call `super(name);` as its first statement.

### `final`

`final class Money { … }` cannot be extended (`E_FINAL_EXTEND`), and a `pub final func` cannot be overridden (`E_FINAL_OVERRIDE`). Use `final` on classes and methods whose behaviour other code relies on.

## Abstract Classes

An `abstract class` is a base class that leaves some methods for its subclasses to supply. The classic use is a *template method*: the base class fixes the outline and calls an abstract step:

```loup
#~ @mode(host.scripted)
abstract class Report {
    pub let title: string;

    new(title: string) {
        this.title = title;
    }

    abstract func writeBody(): void;

    pub func show(): void {
        println(i"== {$ this.title $} ==");
        this.writeBody();
        println("== end ==");
    }
}

class SalesReport : Report {
    pub let orders: list[int];

    new(orders: list[int]) {
        super("Sales");
        this.orders = orders;
    }

    pub override func writeBody(): void {
        var total = 0;
        for (cents in this.orders) {
            total = total + cents;
        }
        println(i"{$ this.orders.length() $} orders, {$ total $} cents");
    }
}

class InventoryReport : Report {
    pub let items: dict[string, int];

    new(items: dict[string, int]) {
        super("Inventory");
        this.items = items;
    }

    pub override func writeBody(): void {
        for (name in this.items.keys()) {
            println(i"{$ name $}: {$ this.items[name] $}");
        }
    }
}

let reports: list[Report] = [
    SalesReport([1250, 899, 4000]),
    InventoryReport({"bolts": 120, "nuts": 75}),
];
for (report in reports) {
    report.show();
}
```

*Listing 8-5: An abstract class with a template method*

```text
== Sales ==
3 orders, 6149 cents
== end ==
== Inventory ==
bolts: 120
nuts: 75
== end ==
```

The Language Reference makes `override` optional when implementing an abstract method; the 1.25 compiler requires it, so write it.

> ⚠ **Planned.** Abstract classes are only partly implemented in Loup 1.25. An abstract method that returns a value is rejected — `abstract func area(): float;` reports `error[E0001]: method 'area' may not return a value on all code paths` — and the compiler does not yet report `E_ABSTRACT_INSTANTIATION` (creating an abstract class) or `E_ABSTRACT_NOT_IMPLEMENTED` (a subclass that leaves an abstract method out), both specified in §1-8.3. Until then, use abstract methods that return `void`, and express a contract with return values as an interface (Chapter 9), which the compiler checks completely.

## Equality

Objects compare by identity unless the class defines `==`. To compare by contents, implement `Equatable` and write the operator:

```loup
#~ @mode(host.scripted)
class Point implements Equatable[Point] {
    pub let x: float;
    pub let y: float;

    new(x: float, y: float) {
        this.x = x;
        this.y = y;
    }

    pub operator func ==(other: Point): bool {
        return this.x == other.x and this.y == other.y;
    }
}

let a = Point(1.0, 2.0);
let b = Point(1.0, 2.0);
println(a == b);
println(a != Point(0.0, 0.0));
```

```text
true
true
```

`!=` is derived from `==`.

### Mistake: an operator without its interface

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

class Point {
    pub let x: float;
    pub let y: float;

    new(x: float, y: float) {
        this.x = x;
        this.y = y;
    }

    pub operator func ==(other: Point): bool {
        return this.x == other.x and this.y == other.y;
    }
}
```

```text
main.lpscr:12:5: error[E_OPERATOR_NO_INTERFACE]: E_OPERATOR_NO_INTERFACE: operator func '==' on 'Point' requires `implements Equatable` on the enclosing type
```

Every operator belongs to an interface (`==` to `Equatable`, `+` to `Add`, and so on; Chapter 9), and the class must say it implements it.

> **Known gap.** Collections do not use a class's `==` yet. In 1.25, `list.contains`, `dict` keys and `set` elements compare objects by identity even when the class implements `Equatable` (and `Hashable`), so `[Point(1.0, 2.0)].contains(Point(1.0, 2.0))` is `false`. Use strings, numbers or structs as keys, or search with a loop and `==`.

## Dynamic Classes

Most classes have a fixed set of fields. A `dynamic class` can also carry fields added at run time — useful for loosely structured data such as settings read from a file. Dynamic fields are reached with `obj!{name}`, never with a dot (§1-8.7):

```loup
#~ @mode(host.scripted)
dynamic class Settings {
    pub let app: string;

    new(app: string) {
        this.app = app;
    }
}

let settings = Settings("indexer");
settings!{timeout} = 5000;
settings!{"log-level"} = "debug";

let timeout: int = settings!{timeout};
println(timeout + 1);
println(settings!{"log-level"});
println(settings.hasField("retries"));
println(settings.fieldNames());
```

```text
5001
debug
false
["timeout", "log-level"]
```

A name with characters that are not valid in an identifier goes in quotes: `!{"log-level"}`. Reading a field that was never set throws `KeyError`; check first with `hasField`. Assigning a dynamic read to a typed binding (`let timeout: int = …`) converts it from `Any`.

### Mistake: reading a dynamic field with a dot

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

dynamic class Settings {
    pub let app: string;
    new(app: string) { this.app = app; }
}

let settings = Settings("indexer");
settings!{timeout} = 5000;
println(settings.timeout);
```

```text
main.lpscr:10:9: error[E0001]: type 'Settings' has no member 'timeout'
```

A dot only ever reaches declared members. The different spelling is deliberate: `settings.app` is checked by the compiler, `settings!{timeout}` is looked up at run time. Write `settings!{timeout}`.

> **Known gap.** §1-8.7 says a typed read such as `let t: int = settings!{timeout}` throws `TypeError` when the stored value has another type. In 1.25 the conversion is not checked: a string stored under `timeout` is bound to `t` unchanged.

Prefer a regular class or a struct whenever you know the fields in advance. In `host.dual` a `dynamic class` draws the warning `W_ANY_IN_AOT_MODE`, and in `freestanding` it is an error.

## Releasing Resources

Class instances are reference-counted in every mode (§1-8.1.1): an object lives while at least one reference to it exists. Memory takes care of itself, but a file, a lock or a temporary directory should be released at a known point. Loup gives you two tools:

- **`with`** and the `Closeable` interface: a class with a `close()` method that `implements Closeable` can be used in a `with` block, and `close()` runs when the block ends, even if it ends with an exception.
- **`del x`**: ends the binding `x` and runs the object's `finalize()` method, if it has one (§1-6.11, §1-8.9).

```loup
#~ @mode(host.scripted)
class Session implements Closeable {
    pub let name: string;

    new(name: string) {
        this.name = name;
        println(i"open {$ name $}");
    }

    pub func close(): void {
        println(i"close {$ this.name $}");
    }
}

class ScratchFile {
    pub let path: string;

    new(path: string) {
        this.path = path;
        writeText(path, "scratch data");
    }

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

with (session = Session("db")) {
    println(i"using {$ session.name $}");
}

let scratch = ScratchFile("scratch.tmp");
println(exists(scratch.path));
del scratch;
println("done");
```

*Listing 8-6: `with` closes a `Closeable`; `del` runs `finalize()`*

```text
open db
using db
close db
true
finalize scratch.tmp
done
```

> **Known gap.** Under the reference-counting contract, `finalize()` also runs when the last reference goes away on its own — for example, when a function that created the object returns. The 1.25 interpreter runs `finalize()` only for `del`. Use `with` or `del` whenever cleanup matters.

Because objects are reference-counted, two objects that refer to each other keep each other alive. Break such cycles with a `weak` field; Chapter 19 covers `weak`, `with` and `defer` together.

## Classes That Scripts Cannot Use

### `unique class`

A `unique class` has instances that can never be shared, only moved. It belongs to the native and freestanding modes:

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

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

```text
main.lpscr:3:1: error[E_UNIQUE_CLASS_ARC_BINDING]: E_UNIQUE_CLASS_ARC_BINDING: `unique class 'Token'` is only valid in `@mode(native)` or `@mode(freestanding)`
```

(The message uses the retired name `native`; the mode is `host.native`.) In a script, use an ordinary class, or a `unique let` binding (Chapter 19). The Loup Native Book covers `unique class`.

### `let` fields are not yet enforced

A `let` field is meant to be fixed after construction. Loup 1.25 does not enforce that yet (candidate ruling FIELD-2):

```loup,not_desired_behavior
#~ @mode(host.scripted)
class Account {
    pub let id: string;

    new(id: string) {
        this.id = id;
    }

    pub func rename(newId: string): void {
        this.id = newId;
    }
}

let account = Account("A-1");
account.rename("B-2");
println(account.id);
```

```text
B-2
```

The method assigns a `let` field and is not even a `modifier`; the compiler accepts it. Treat `let` fields as fixed anyway — a future release will reject this code.

> ⚠ **Planned.** Annotations such as `@deprecated("use fetchJson")` are specified to produce a warning wherever the marked declaration is used (§1-8.8). In 1.25 the compiler warns `Unknown annotation '@deprecated'` at the declaration and nothing at uses. Reading annotations at run time belongs to `core.reflect` (Chapter 14), which is not implemented.

## Summary

- A class declares fields with `let` or `var` (`pub` to expose them), at most one `new` constructor, and methods that reach the object through `this.`.
- Objects are shared by reference; `==` compares identity unless the class implements `Equatable` and defines `operator func ==`.
- A method that assigns a `var` field is `modifier`; calling it, or assigning a `pub var` field from outside, needs `safe { … }` or a `safe func`; a function that changes an object it receives takes it as `edits`.
- Without `new`, the generated constructor takes the fields by name, parent fields included; defaulted fields may be left out.
- Alternative constructors are `static` factories, which may return `Result`.
- Non-`pub` fields are private to the class and its subclasses; non-`pub` methods are private to the file.
- Inheritance is single: `super(args)` in the constructor, `override` on replaced methods, `super.m()` to call the parent's version, `final` to forbid extension.
- Abstract classes work for `void` abstract methods today; use interfaces for contracts that return values.
- `dynamic class` adds run-time fields reached with `!{name}`.
- `with` + `Closeable` and `del` + `finalize()` release resources at a known point.

## Try It Yourself

1. Write a `RateLimiter` class with a `pub var` list of request timestamps and a `modifier` method `allow(now: int): bool` that permits at most three requests in any ten-second window. Show that two limiters do not share state.
2. Give `Endpoint` (Listing 8-3) a factory `fromText(text: string): Result[Endpoint, string]` that splits `"host:port"`, and print both a good and a bad input.
3. Extend Listing 8-4 with a `Dog : Animal` class. Then remove `override` from its `speak()` and read the error.
4. Write a `TempDir` class that implements `Closeable`, creates a file in its constructor and prints a message in `close()`. Use it in a `with` block that throws, catch the exception outside, and confirm that `close()` ran first.
