Objects and Vectors
Vectors
Section titled “Vectors”let nums = [1, 2, 3, 4]
print(nums[0])print(nums[3])The element type is inferred from the literal. [1, 2, 3] is Vec<int>;
[1.0, 2.5] is Vec<number>. Mixing element types in a single literal is a
compile error — every element must share a concrete type.
Index Access and Bounds
Section titled “Index Access and Bounds”arr[i] reads the element at zero-based index i. Indexing out of bounds
currently raises a runtime error rather than returning None:
let items = [10, 20, 30]
print(items[5]) // Runtime error: Index 5 out of bounds (length 3)Use .first() / .last() when you want the first or last element, or
guard with i < arr.len() before indexing. The arr[-1] shorthand for
“last element” is NOT supported — use arr.last() instead.
let items = [10, 20, 30]
print(items.first())print(items.last())print(items.len())Lifting bounds-checked access to return Option<T> is a v0.4 candidate; cite
v0.4-vec-indexing-option-returns in the close-summary follow-up tracker.
Slicing
Section titled “Slicing”Index expressions accept Rust-style range syntax for closed-range slice access:
let nums = [10, 20, 30, 40, 50]
print(nums[1..4])print(nums[0..2])Half-open ranges (nums[1..], nums[..3], nums[..]) and the inclusive
form (nums[1..=3]) are recognized by the grammar but do not yet match the
behavior the syntax suggests — half-open forms fail to parse inside
arr[..], and ..= truncates one element off the right end. Use the closed
form (start..end) until those paths are tightened up at v0.4. Cite
v0.4-vec-slice-half-open and v0.4-vec-slice-inclusive-end in the
follow-up tracker.
Common Operations
Section titled “Common Operations”let nums = [1, 2, 3, 4]let doubled = nums.map(|x| x * 2)let evens = nums.filter(|x| x % 2 == 0)
print(doubled)print(evens)print(nums.first())print(nums.last())print(nums.len())Spread
Section titled “Spread”Object spread (...) inlines the contents of one object into another. Later
keys override earlier ones:
let base = { x: 1, y: 2 }let extended = { ...base, z: 3 } // intended: { x: 1, y: 2, z: 3 }let overridden = { ...base, x: 99 } // intended: { x: 99, y: 2 }Object spread parses today but emits an inferred field type that the strict
post-inference verifier rejects (E0900 per the W17 FieldType::Any boundary
audit at docs/cluster-audits/v0.3-w17-fieldtype-any-boundary-audit.md).
Rebuilding the spread-merge expression so it lands on a typed inferred
schema is a v0.4 candidate; cite v0.4-object-spread-typed-inference in
the follow-up tracker.
Array spread ([...other, extra]) parses but hits the V3-S5 ckpt-5
consumer-cascade surface during construction (the deleted typed-array-data
enum + Buf<T> carrier rebuild lands at ckpt-6 STRICT close per ADR-006
§2.7.24 Q25.A SUPERSEDED + docs/cluster-audits/w12-typed-array-data-deletion-audit.md):
let base = [1, 2, 3]let extended = [...base, 4, 5] // SURFACE: V3-S5 ckpt-5Cite v0.4-v3-s5-ckpt-6-strict-close in the follow-up tracker. Use
.concat() for append today:
let base = [1, 2, 3]let extended = base.concat([4, 5])print(extended)List Comprehensions
Section titled “List Comprehensions”Build a vector from an iterator with optional filtering. The parser accepts the syntax, but every construction site funnels through the same V3-S5 ckpt-5 op_new_array SURFACE as array spread above:
let squares = [x * x for x in 0..5] // SURFACE: V3-S5 ckpt-5let evens = [x for x in 0..10 if x % 2 == 0] // SURFACE: V3-S5 ckpt-5let pairs = [{ i: i, j: j } for i in 0..3 for j in 0..3 if i != j]Cite v0.4-v3-s5-ckpt-6-strict-close (same workstream as array spread). Use
.map / .filter on an existing vector today:
let xs = [0, 1, 2, 3, 4]let squares = xs.map(|x| x * x)let evens = xs.filter(|x| x % 2 == 0)
print(squares)print(evens)Mapping a bare Range ((0..5).map(...)) does not yet thread the iterator
element type through to the closure parameter; that surface lands alongside
the same v0.4 inference push. Cite v0.4-range-map-element-type-inference
in the follow-up tracker.
Objects
Section titled “Objects”Anonymous objects are dynamic key-value containers. Fields are accessed by name:
let user = { id: 1, name: "Ada"}
print(user.name)print(user.id)Adding a NEW field to an existing binding (user.score = 99) is a compile
error: “Assignment to ‘user.score’ requires compile-time field resolution.”
Rebuild via merge instead (see below) or use a typed struct (next section).
Nested Objects
Section titled “Nested Objects”let cfg = { server: { host: "localhost", port: 9091 }}
print(cfg.server.host)Object Merge
Section titled “Object Merge”The + operator merges two anonymous objects, right side winning on key
conflicts:
let a = { x: 1, y: 2 }let b = { z: 3 }let c = a + b
print(c)Merging when both sides share a key currently surfaces a type-inference gap
(string + {key} fails inference even though the runtime semantics are
“right wins”); use disjoint-key merges today. Cite
v0.4-object-merge-overlapping-key-inference in the follow-up tracker.
let user = { id: 1, name: "Ada" }let renamed = user + { name: "Grace" } // inference: string + {name}Typed Objects
Section titled “Typed Objects”For fixed-shape records, declare a type with named fields. Typed-object
field access is statically checked, generates direct field-offset reads,
and runs identically under VM and JIT:
type Point { x: int, y: int }
let p = Point { x: 3, y: 4 }
print(p.x)print(p.y)print(p.x + p.y)Nested typed structs presently surface a v0.3-vintage construction-site
ordering issue; until that lands, prefer anonymous nesting ({ server: {...} },
as in Nested Objects). Cite
v0.4-typed-object-nested-construction in the follow-up tracker.
Vector Methods
Section titled “Vector Methods”Vec<T> carries its element type through method chains. The methods below
are pure-Shape implementations defined in crates/shape-runtime/stdlib-src/core/vec.shape
(verified at HEAD), with a few delegating to runtime intrinsics where noted.
| Method | Signature | Description |
|---|---|---|
first() | () -> T | First element (runtime error if empty) |
last() | () -> T | Last element (runtime error if empty) |
len() | () -> int | Element count |
push(x) | (T) -> void | Append in place (requires let mut) |
pop() | () -> T | Remove and return last element |
map(f) | (T) -> U → Vec<U> | Transform each element |
filter(p) | (T) -> bool → Vec<T> | Keep matching elements |
find(p) | (T) -> bool → T | First match |
findIndex(p) | (T) -> bool → int | Index of first match, -1 if none |
some(p) / every(p) | (T) -> bool → bool | Existential / universal check |
includes(v) | (T) -> bool | Membership |
indexOf(v) | (T) -> int | First index of value, -1 if none |
forEach(f) | (T) -> void → void | Side-effect iteration |
concat(other) | (Vec<T>) -> Vec<T> | Append another vector |
take(n) / drop(n) | (int) -> Vec<T> | First / skip-first N elements |
slice(s, e) | (int, int) -> Vec<T> | Sub-range as new vector |
reverse() | () -> Vec<T> | Reversed copy |
clone() | () -> Vec<T> | Shallow copy |
join(sep) | (string) -> string | Join elements with separator |
Type preservation: filter, take, drop, slice, concat, reverse,
clone all return the same Vec<T> — the element type flows through the
chain.
reduce(f, init), sort(cmp), flatMap(f), and groupBy(key_fn) are
defined in the same vec.shape module but presently surface either VM/JIT
divergence (reduce JIT-side fold-state, tracked under v0.4-vec-reduce-jit-fold-state)
or a comparator type-inference gap (sort with |a, b| a - b does not
resolve the operand kinds; tracked under v0.4-vec-sort-comparator-inference).
Prefer a manual for loop or forEach if you hit one of these today.
Numeric-only methods — sum(), avg(), mean(), min(), max(),
std(), variance(), dot(other), norm(), normalize(), cumsum(),
diff(), abs() — are provided via impl NumericVec for Vec for
Vec<number> / Vec<int> receivers.
HashMap
Section titled “HashMap”HashMap<K, V> is an ordered key-value map. Keys are typically strings;
integer-keyed maps work end-to-end but hit a string-key assertion in some
internal paths today. Insertion order is preserved.
let m = HashMap().set("a", 1).set("b", 2).set("c", 3)
print(m.get("b"))print(m.has("a"))print(m.len())print(m.isEmpty())HashMap is immutable — set and delete return a new HashMap rather
than mutating in place. Chain the calls on a single let binding or use
let mut and rebind:
// Compile error: bare `m.set(...)` after `let m = HashMap()` is rejected// by the strict-typing borrow analyzer.let m = HashMap()let m2 = m.set("a", 1)// Chain on a single binding:let m = HashMap().set("a", 1).set("b", 2).set("c", 3)print(m.get("b"))print(m.len())HashMap Methods
Section titled “HashMap Methods”| Method | Signature | Description |
|---|---|---|
get(key) | K → Option<V> | Look up by key |
set(key, value) | (K, V) → HashMap<K,V> | Return new map with entry added/updated |
has(key) | K → bool | Check if key exists |
delete(key) | K → HashMap<K,V> | Return new map without key |
len() | () → int | Number of entries |
isEmpty() | () → bool | True if no entries |
map(f) | (K, V) → U → HashMap<K, U> | Transform values |
filter(p) | (K, V) → bool → HashMap<K, V> | Keep matching entries |
forEach(f) | (K, V) → void → void | Iterate over entries |
keys(), values(), and entries() are defined in
crates/shape-runtime/stdlib-src/core/hashmap_methods.shape but their
runtime path currently surfaces the V3-S5 ckpt-5 consumer-cascade tier-3
surface (the Arc<TypedArrayData> result carrier was deleted across
ckpt-1..ckpt-4 per ADR-006 §2.7.24 Q25.A SUPERSEDED; rebuild lands at
ckpt-6 STRICT close). Cite v0.4-v3-s5-ckpt-6-strict-close in the
follow-up tracker.
Destructuring
Section titled “Destructuring”Object destructuring is the load-bearing path for binding several fields at once. The grammar accepts it:
let point = { x: 3, y: 4 }let { x, y } = point // VM raises a TypedObject-exception machinery surface todayprint(x)print(y)The VM destructuring path currently raises a Phase-2c TypedObject-exception
machinery surface (AnyError TypedObject build / trace-frame build pending
re-emission on the kinded Arc<TypedObjectStorage> model per ADR-006
§2.7.4); JIT works. Use direct field access (obj.field) until
the destructuring path re-lands at ckpt-6 STRICT close. Cite
v0.4-vm-destructure-typedobject-exception in the follow-up tracker.
let user = { name: "Ada", age: 30 }
print(user.name)print(user.age)- Anonymous and typed-object field access are both statically checked.
- Vectors and objects compose naturally with
match, lambdas, and table transforms. - HashMap preserves insertion order and uses immutable semantics.
- All snippets above without an explicit
runnable=falselabel are byte-exact VM == JIT at HEAD (W15.2-LABEL Phase A convention; citedocs/cluster-audits/v0.3-w15-label-phase-a-close.md§1).