Collections
Arrays
Fixed-size, stack-allocated, bounds-checked.
let arr = [10, 20, 30]
print(arr[0])
print(arr.len)
// Repeat syntax
let zeros = [0; 100] // 100 zeros
// Mutable arrays
var buf: [u8; 8192] = [0; 8192]
buf[0] = 42Out-of-bounds access is a runtime panic, not silent corruption.
Vec<T>
Dynamic array that owns its elements. Freed when it goes out of scope.
var v: Vec<i32> = Vec.new()
v.push(10)
v.push(20)
v.push(30)
print(v[0]) // bounds-checked
print(v.len)
let last = v.pop() // removes and returns last elementvar names: Vec<string> = Vec.new()
names.push("Alice")
names.push("Bob")
print(names[0])HashMap<K, V>
Open-addressing hash table with FNV-1a hashing.
var m: HashMap<string, i32> = HashMap.new()
m.insert("hello", 42)
m.insert("world", 99)
print(m.len)
if m.contains("hello") {
print("found it")
}
let val = m.get("hello") // returns Option<i32>
if let Option.Some(v) = val {
print("value: ", v)
}
m.remove("hello")Heap<T>
Single-owner heap pointer. Three use cases: recursive types, runtime polymorphism via interfaces, and values that need to outlive their creating scope.
Recursive data structures
A struct or enum can't contain itself directly (infinite size). Wrap the recursive case in Heap<T> to make it pointer-sized.
enum Tree {
Node(Heap<Tree>, Heap<Tree>),
Leaf(i32),
}
fn sum(t: Tree): i32 {
match t {
Tree.Leaf(n) => { return n }
Tree.Node(left, right) => {
return sum(*left) + sum(*right)
}
}
return 0
}
let tree = Tree.Node(
Heap(Tree.Leaf(1)),
Heap(Tree.Leaf(2))
)
print(sum(tree)) // 3Runtime polymorphism
Heap<Interface> lets you store different concrete types in the same collection. The heap pointer carries an itable for virtual dispatch.
interface Shape {
fn area(self: &Self): f64
}
struct Circle { radius: f64 }
impl Circle {
fn area(self: &Self): f64 { return 3.14159 * self.radius * self.radius }
}
struct Square { side: f64 }
impl Square {
fn area(self: &Self): f64 { return self.side * self.side }
}
fn main(): i32 {
var shapes: Vec<Heap<Shape>> = Vec.new()
shapes.push(Heap(Circle { radius: 5.0 }))
shapes.push(Heap(Square { side: 4.0 }))
for s in shapes {
print(s.area())
}
return 0
}Dereference
Use * to read through a heap pointer:
let h = Heap(42)
print(*h) // 42Methods are called directly — no * needed:
let s: Heap<Shape> = Heap(Circle { radius: 3.0 })
print(s.area()) // auto-derefs through Heap, then dispatches via itableHeap<T> vs Arena<T>
Heap<T> is single-owner: one value, one pointer, freed on drop. Arena<T> is pool-based: many values in one allocation, referenced by copyable handles. Use Arena when you have graphs, caches, or cycles where ownership doesn't form a tree.
All heap types (Vec, HashMap, Heap) auto-free when they go out of scope. No GC pauses, no free(), no defer.
Next: Strings →