A slice copies. A span reads someone else’s elements
in place: a value of three words — a data pointer and two bounds — that
walks storage it does not own. It is the same view a
conformant parameter has always carried;
span of T is that view’s positional spelling, and 7.0.0 lets it stand in
var, parameter and return positions.
The example is
examples/Spans.
Build and run it:
make -C examples/Spans runBorrowing a sub-range
var
xs : span of int64;
xs := Span(series, 2, 5);
WriteLn(" Sum(Span(series, 2, 5)) = %lld", Sum(xs));Three words move, zero elements do — and the borrowed window admits anywhere the conformant view admits, including every math verb. The proof it is a view and not a copy is one edit:
series[3] := 100;
WriteLn(" after series[3] := 100 the same span sums to %lld", Sum(xs));The span sees the new value, because there is no copy to go stale.
The view verbs and the lending law
RowView answers a matrix row as a span; WindowView answers count
elements of a vector from start. Both copy nothing — and both spell the
lending law in their signatures:
row := RowView(address m, 1);
Sum(WindowView(address v, 2, 3));A view that escapes a call must come from storage the caller visibly lent.
So a view producer takes its source through a pointer — address m, the
same visible-mutation marker every write path in Mica wears — and the
caller keeps m alive while the span is read. Forget the address and
the inference diagnostic teaches the spelling back (5446, naming
'address m').
The payoff is the allocation-free per-axis reduction:
Sum(RowView(address m, 2)) / 4.0 { row 2's mean: one line, zero copies }What a span refuses
The borrow stays scope-shaped, and every escape is refused at compile time, by name:
| The program tried to | The answer |
|---|---|
| a span in a record field or array element | a span cannot stand in <position>: a span is a borrowed view whose backing may die before the position it would rest in |
| a span as a task parameter | the same durable-position refusal — a task’s frame outlives its scheduling site |
| return a span rooted in a local | the return-root rule: a returned span roots in the frame’s own parameters |
| write through a span | span of T is the read face by law; writing keeps the explicit pointer array[lo..hi : int64] of T binder spelling |
| grow a dynamic array while a named window borrows it | refused (5488) — growth moves the backing, so the window is admitted and the growth is not |
Empty is honest
WindowView(address v, 0, 0) answers the empty span: walks run zero
times, Sum answers zero, and the champions
fail as absence — no sentinel values anywhere.
What this does not do
- No writable span. One face, the read face. The writable view keeps
its pointer-spelled binder, so every write path still says
address. - No span of spans, no span fields. The depth rule follows one borrow, and positions it cannot follow are refused rather than documented.
- No lifetime annotations. The lending law is structural — the
addressat the call site is the annotation, and the reader sees it.
Try it
Delete the address from RowView(address m, 1) and read the diagnostic
— it names the lending spelling. Then try storing xs into a record field
and meet the durable-position refusal.
Next
Spans are how the reduction verbs serve every one-dimensional shape with one signature — and the runtime-length layer the whole math unit is built on.