Bytes Is the Language’s, and the Tether It Needs Is Refused Until It Is BuiltStatus: Superseded by ADR-283 — its D1-D3 are D19-D21 there, D6 is D22, D5’s polarity is D13’s; D4 is not carried (displaced by ADR-209) Date: September 20, 2026 Target Version: Nikaia 0.0.71 Related: ADR-154 D1 and §4 (the prelude’s list, and the door this closes), ADR-008 D2, D5, D9 (the three states and the residual hard error), ADR-140 (one spelling per thing), Part I 1.3, Part I 6.6, Part III 17.1, Part III 17.2, Part III C.1, Part III C.4
ADR-154 D1 puts Bytes on Part I 1.3’s list, beside Vec and
String, as the containers a program cannot do without. §4 of that record
left open whether it is std’s or the language’s — it is in the list because a
program cannot do without it, wherever it lives. Enforcing the list (§5) is
what turned that door into a question: what needs no use is what std’s
ledger keys bare, so the two answers stopped being interchangeable. One of
them puts Bytes in a module and behind a use, which is the opposite of being
on the list.
And the name reached nothing. Bytes did not exist — not in std, not in
the compiler’s built-in names, not in any .nika file. A name on a prelude’s
list that does not exist is the one direction a prelude can be wrong in without
anybody noticing, because nothing refuses it: nothing gets that far.
It was never a missing name, either. Part III 17.2 already says what it is:
read returns Bytes, not a Vec[u8]: it is one shared buffer, and slices
that outlive its scope are tethered to it (Part I 6.6). So Bytes is the
container the tether needs — and the tether is the half of Part I 6.6 that
is not built. ADR-008 D2’s three states are Borrowed ⊑ Tethered ⊑
Owned; the first is the language below’s own lifetime, the third is
.to_owned() written by the program, and the middle one is a reference-counted
buffer that does not exist. What exists in its place is D5’s residual hard
error.
That is the shape of the question this answers: a type whose surface is specified, whose mechanism is not, and whose absence from the tree is the only reason nothing is broken.
Bytes is a language type, written bare.It is a name this compiler
knows, like Vec, String and Array, and it stays on Part I 1.3’s list. Not
std’s: a type whose representation the compiler picks — which is what
Part I 6.6’s states are — is not a type a module owns, and putting it behind a
use would make fs::read’s result a name a program must import before it can
write down what it already holds.
A Bytes is a reference-counted, immutable run
of bytes: handing one on costs a count, never a copy. It reads as a run of bytes
everywhere a &[u8] is taken, so nothing written against the language below has
to know what it is. Building bytes is a Vec[u8] handed over, which is the one
copy in the type and the program’s to write.
fs::read hands one back.Which is what Part III 17.1 and 17.2 have
said all along and the ledger did not: it keyed Vec[u8]. Reading a file and
passing it on now costs a reference count rather than a copy of the file.
NK2303.This is the
mechanism Bytes hangs on, and it is not built, so the compiler says so in the
one place a program can act on: the Nikaia line. Where a function hands back a
view that points into a buffer its own body made, the program is refused, with
the buffer named and the two ways out — take the buffer as a parameter, or hand
back a copy with .to_owned(). The compiler never inserts that copy
(ADR-008 D5).
The
analysis errs towards Tethered, which is ADR-008 D7’s own
polarity: a state it cannot decide is the wider one, and being wrong costs a
representation wider than it had to be. A refusal may not err that way.
Refusing a correct program is the worse of the two mistakes
(Part III C.4), so NK2303 fires only where the walk can name the buffer’s type
and the returned expression is derived from the local that holds it. A call no
ledger describes raises nothing.
Mapped does not deref to Bytes yet.Part III 17.2 says it does, and
that sentence stays a promise rather than becoming a lie in code: fs::map
hands back a mapping whose deref is &str today, which is what
examples/1brc.nika parses. Changing it is the tether’s own work, where a
mapping and a read really do become the same thing to a parser.
Part I 1.3’s list is true for one more name. Two are left — assert and
panic — and both belong with Part III 14, which is not built. The Status note
on that section says so and now says it about two names instead of three.
docs/open-decisions.md loses its Bytes entry, which is what an answer
does to a question: the entry leaves rather than staying with a note on it. The
page recommended C now, B when the tether is built; this record is B, built
now, with D4 in the place of the mechanism. The reason is the cost the page
itself named for C: a program written between now and the tether would write
Vec[u8] and get a second spelling for one thing when Bytes arrived,
which is the problem ADR-140 spent a whole record removing.
A shape rustc used to speak about now has a Nikaia message. A function
that hands back a view of a local buffer lowered to Rust that does not compile,
and the diagnostic named a type in a generated file. Part III C.1 says rustc
must never speak about that file; D4 is where that stops for this shape.
Nothing in the tree is refused by it. Every view in every signature of
examples/ and benches/ solves to Borrowed — ADR-008 §3’s free
case, measured — and the_corpus_needs_no_tether in
crates/nikaia/tests/bytes.rs is that claim held to, over every .nika file
in the repository.
views column. #196 carries
it.Bytes offers a program. len, text and a copy out are what
fs::read’s result needs to be useful; slicing, concatenation and the rest
wait for a program that wants them.Mapped’s deref (D6), which is the tether’s work and not this record’s.eprint, the smaller question that rode with the entry this record
closes: it is in the compiler and not on D1’s list. It stays in
docs/open-decisions.md as an entry of its own.Bytes is in the compiler’s built-in type names and in std’s
prelude, so a program writes it bare and the generated Rust resolves it.crates/nikaia-std/src/bytes.rs — Arc<[u8]> behind a type with
len, is_empty, text, to_vec, a deref to [u8], and the conversions
in. Four unit tests, one of which is that a clone shares the allocation.fs::read hands back a Bytes, in std’s ledger and in std.NK2303, in crates/nikaia/src/contracts/tether.rs, run from
the checker beside NK2302’s walk. Fifteen tests in
crates/nikaia/tests/bytes.rs, six of which are programs it must not
refuse.Mapped’s deref to Bytes (D6).