Where Clauses
A where { ... } clause attaches runtime constraints to a declaration: a braced
block of boolean predicates, comma-separated with optional newlines (a trailing
comma is fine). Every predicate must typecheck as bool, and all of them must
hold (they are ANDed).
Placement
| Construct | Position | Checked |
|---|---|---|
| Functions and methods | between the parameter list and returns | at entry |
| Lambdas | between the parameter list and returns | at entry |
| Class fields | after the field declaration | at .new() and on every assignment |
| Classes (invariants) | after the class body's closing brace | at .new() and on assignments to referenced fields |
| Interface methods | after the signature | at entry of every implementing method |
| Enum variants | after the payload | at construction |
Class-level invariants refer to fields by bare name (port, not self.port).
Interface preconditions are written against the interface's parameter names and
are enforced by every implementing class, even when the implementation renames
the parameter.
Violations panic
A failed predicate panics through the unified panic path and points at the failing predicate:
panic: where constraint violated
--> server.mux:3:27Objects are born valid
Invariants are checked when .new() runs, after field defaults are applied.
A constrained field therefore needs a default that satisfies its constraints:
int port where { port > 0 } without a default zero-initializes to 0 and
would panic at construction (and, since the violation is provable, is rejected
at compile time - see below).
Provable violations are compile errors
When the compiler can prove a predicate fails from literal values alone, the program is rejected at compile time instead of panicking at runtime:
Anything the compiler cannot prove (variables, method calls in predicates, generic values) stays a runtime check. There are no warnings: a check either fails compilation because the panic is certain, or it runs at runtime.