proc main(): void {
v := ["a", "b", "c"]
echo v[2] // a literal into a known length
last := len(v) - 1
if v bounds last {
echo v[last] // the guard covers it
}
if v bounds last && v[last] == "c" {
echo "and the rest of its own condition"
}
for i := 0; i < len(v); i++ {
echo v[i] // the loop's own condition is the guard
}
if indexOf(v, "b") is Result.Ok(at) {
echo v[at] // a position indexOf answered with
}
}func cell(t: Str[dyn][dyn], r: Int, c: Int): Str {
// `if t bounds r { if t[r] bounds c { echo t[r][c] } }` is refused:
// t[r] is a different row each time r moves, so it is bound first.
if t bounds r {
row := t[r]
if row bounds c {
return row[c]
}
}
return ""
}
proc main(): void {
Str[dyn][dyn] t = [["a", "b"], ["c", "d"]]
echo cell(t, 1, 0)
}Every index and slice is proved in range before the program runs, so none can fail when it does; one the compiler cannot prove is a compile error. What proves one:
v[2] on a Str[3].if v bounds i { … }, which is i >= 0 && i < len(v). It covers the branch under it and the rest of its own && condition, so if v bounds i && v[i] > 0 is proved; it proves nothing past an ||.len(v).indexOf answered with — but only for the vector it searched.What proves nothing: an index worked out on the spot, like v[len(v) - 1], which has to be bound to a name and guarded; an early return, since a guard proves only the branch it opens; and a cell of a row picked by a moving index, t[r][c], where the row has to be bound first — row := t[r] — and the cell guarded against it. A slice's two bounds are proved one at a time.
A proof lasts until something could change what it was about: assigning to the vector or the index, declaring either again or binding its name in a pattern, drop (the one builtin that shortens a vector), or handing the vector to a mut parameter. Inside a loop, what the body does counts against every turn.