structs & refs

Keys & Equality

Structs as Map and Set keys: integer-only structs compare and hash for free; float-bearing ones declare their own pair.

A struct whose leaves are all integers (recursively, through nested structs and FixedArray) gets a derived == and hash: leaf-wise comparison in schema order, and a deterministic FNV-1a over the leaves. Such structs compare with ==/!= anywhere and key a Map or Set with no declarations at all.

keys.as
struct Cell {
    x: i32 = 0;
    y: i32 = 0;
    constructor(x: i32, y: i32) { this.x = x; this.y = y; }
}

struct Snapped {
    x: f32 = 0;
    y: f32 = 0;
    constructor(x: f32, y: f32) { this.x = x; this.y = y; }
    operator==(a: Snapped, b: Snapped): bool {
        return i32(a.x * 16) == i32(b.x * 16) &&
               i32(a.y * 16) == i32(b.y * 16);
    }
    readonly hash(): u32 {
        return u32(i32(this.x * 16)) * 31 + u32(i32(this.y * 16));
    }
}

export function demo(): i32 {
    let grid = new Map<Cell, i32>();
    grid.set(Cell(3, 4), 7);            // derived == and hash

    let snapped = new Map<Snapped, i32>();
    snapped.set(Snapped(1.0, 2.0), 5);  // declared pair: quantized
    let hit = snapped.has(Snapped(1.01, 2.01)) ? 1 : 0;

    return (grid.get(Cell(3, 4)) as i32) + hit;   // 7 + 1
}

Float-bearing structs never derive — -0.0 == +0.0 yet the bits differ, and NaN would make a key unfindable — so they declare the pair explicitly: operator== plus a readonly hash(): u32. Declaring one without the other is a compile error (a custom equality that disagrees with a derived hash is the classic unfindable-key bug), and coherence — a == b implies equal hashes — is your contract, like C++ and Dart. Deviations from stock expectations, all deliberate: