The guide · explanation
wscript: containers, loops, traits and faults
This chapter covers the built-in containers List[T] and Map[K, V], the loop forms, traits and operator overloading, the derives, and the two things every script author needs to know about the runtime: memory is reference counted, and a fault ends the step. The earlier chapters cover values, types and functions and structs, enums and matching.
Lists
List[T] is a growable, ordered sequence. A list literal is [a, b, c]. Indexing with xs[i] faults when the index is out of bounds; xs.get(i) returns an Option[T] and never faults. The combinators map, filter and fold take closures and chain.
let xs = // List[int]
xs.push
xs // 1; faults if out of bounds
xs.get // None
xs.map.filter.fold
let lines = out.split.filter
sort, min, max and contains need an element type that supports the comparison: primitives, containers of them, or a struct with the relevant impl. join needs List[string]. sum needs numeric elements. The full method list, with signatures, is in built-in methods.
Maps
Map[K, V] is a hash map. A map literal is written #{ key: value, ... }. A key is an int, bool, char or string. Reading m[k] faults when the key is missing; m.get(k) returns an Option[V]. Assigning m[k] = v inserts or overwrites.
let ages = # // Map[string, int]
ages = 22 // insert or overwrite
ages // faults
ages.get // None
ages.each
The map combinators take two-parameter closures over key and value: each, map (which returns a List), and filter (which returns a Map). Iterating a map with for yields its keys.
Index or get
xs[i] and m[k] fault on a missing element. Where absence is expected, and in a resource it almost always is, call .get(...) and handle the Option.
Strings as containers
A string is immutable. split returns a List[string], chars returns a List[char], and a for loop over a string yields its characters. slice(start, end) and find count characters, not bytes. parse_int and parse_float return an Option, so a malformed number is a None rather than a fault.
Loops
The loop forms are for x in iterable, while cond, and loop. break and continue work in all three. A for loop iterates a range, a list's elements, a map's keys, or a string's characters.
for i in 0..10 // exclusive; 0..=10 is inclusive
for x in // list elements
for k in ages // map keys
for c in "abc" // chars
while cond
loop
A range such as 0..n is valid only in a for header. There is no range value to bind to a variable or pass to a function.
Traits
A trait is an interface with Rust syntax and Go-style satisfaction. Dispatch is static when the concrete type is known. A dyn Trait parameter or element selects dynamic dispatch through a vtable, and a concrete type coerces to dyn Trait implicitly at such a typed boundary. There are no default method bodies and no trait inheritance.
}
}
let shapes: List =
Operators and derives
Operators dispatch through built-in traits. Implement the trait on a struct or enum to overload the operator. Compound assignment such as += evaluates its place once and dispatches through the same trait, so a type with an Add impl gets += for free.
- Add, Sub, Mul, Div, Rem give +, -, *, /, %.
- Neg gives unary -.
- Eq gives == and !=. It is required for == on a struct or enum; there is no implicit identity equality. Use same(a, b) to compare references.
- Ord gives <, <=, >, >=. The hand-written form is fn cmp(self, other: Self) -> int, returning -1, 0 or 1.
- Display gives str(x) and string interpolation of x.
- Index gives x[i], read-only.
Vec2
}
}
#[derive(...)] generates the common impls. Eq and Ord derive structural comparison, Display derives a debug-style rendering, and Clone derives a deep copy reached with .clone().
There are no bitwise operators. Write the arithmetic out. wscript's units declaration gives an int or float a family of named units, such as 500ms or 4MiB, stored in a base unit and checked so that two families cannot be added. A unit value is its backing number at runtime, so it reaches a host function, a List or a Value as a plain number.
Memory
Memory is pure reference counting. There is no cycle collector, so a reference cycle leaks until the VM is dropped at the end of the step. Break a cycle with a weak reference: weak(x) makes a weak[T] from a reference value, and w.upgrade() returns Option[T], None once the strong references are gone. Use weak[T] for a back-reference from a child to its parent.
let child = Node
match child.parent Some => match w.upgrade Some => println,
None => println,
}
None =>
}
Faults
A fault is a VM-level error: an index out of bounds, a division by zero, unwrap() on None or Err, or an aliasing violation at the host boundary. Script code cannot catch a fault, and a fault never panics config-weave. It is delivered to the host with a message and a stack trace, one frame per call with file, line and column.
In config-weave a fault ends the step with the Error status. The message and trace appear in the step's log output. A step in Error does not run its apply and does not count as converged, as described in The convergence contract. The rule for avoiding faults is the same throughout this chapter: reach for the Option-returning form wherever a miss is expected, and let a host Result flow out through ? rather than unwrap().
What a fault looks like
The log shows the fault message, for example list index 10 out of bounds (len 3), a snippet at the fault site, and the script stack trace innermost first. Troubleshooting covers reading it.