String, Whose State the Compiler Picks — and &str Is the Assertion That It Is a ViewStatus: Superseded by ADR-282 — its D1-D5 are D1, D2, D7, D26 and D27 there
Date: September 16, 2026
Target Version: Nikaia 0.0.8
Refined by: ADR-216 — D3’s copy is written .clone(); .to_owned()
is refused naming it, and .to_string() stays as the text form of a value
Related: ADR-008 (the three states of a view, and
@borrowed), ADR-005 §3 (no copy the program did not write),
ADR-094 D2 (&T in a declaration is an assertion the body is
held to), ADR-027 (the same pattern for sync),
ADR-058 D1 (Bytes, the buffer this makes text behave like),
ADR-104 (the foreign boundary, where a copy may be owed)
A view is in one of three states — borrowed, tethered, owned — and the
compiler picks the cheapest that works; a program never writes the state
(Part I 6.6). Bytes is a buffer that behaves that way and a program
cannot tell which state it is in. Text is the one place where the program
picks the state itself, in the type: String owns, &str views, and a
literal put where a String is declared is refused until .to_string()
copies text the program already had. Thirteen times in examples/, every
one a literal or a view where a String was declared.
The language’s pattern for the compiler decides, and you may pin it is
already in three places: sync is inferred and a written sync is held to
(ADR-027); @borrowed on a struct turns a tether into a
refusal; &T on a parameter is a view the body may not keep
(ADR-094 D2). Text gets the same pattern.
String is the one text type, and its state is the compiler’s.A String value is borrowed, tethered or owned by 6.6’s rule — the
cheapest state that works, chosen per use — and the program never names
the state. A literal stored in a String field is a view of static text
and allocates nothing; content_type: "text/plain" is a program. f"…"
still builds text, and it is owned because nothing else can hold it.
&str is the assertion that a value is a borrowed view, and it is held to.Writing &str — on a parameter, a field, a result — says this must be a
borrowed view: no copy, no handle. Where the compiler would need a tether
or an owned copy to make the program work, the program is refused at the
line that needs it, naming what would have been kept. This is
ADR-094 D2’s &T for text and ADR-008 D6’s
@borrowed per field, with the spelling the language already has. &str
is therefore not a second type; it is String with a promise attached.
ADR-005 §3 stands. .to_owned() is how a program asks for
text of its own — to stop a twelve-byte name pinning a mapped file, or to
hand text to something that must own it. Where a copy would be needed and
none is written, the compiler refuses and names it. .to_string() is the
same call under the name a reader from another language will type.
nikaia explain --tethers prints, for every text value, which state it
got and why (Part I 6.6). A hot loop that must not allocate asserts it with
&str or @borrowed, and reads the report to see that it did.
String there is a copy the program writes.A described crate’s entry (ADR-104) says &str or String
in Rust’s terms. A Nikaia String in any state crosses to Rust &str for
free. Crossing to Rust String needs text the callee may keep, so it is
.to_owned() in the program or a refusal naming it (D3).
Every .to_string() that existed to satisfy a type goes; the ones that
mean copy this stay, spelled .to_owned(). NK1106’s help line stops
saying write .to_string() about a literal. Part III 15.2’s two text rows
become one.
What a text value costs: a pointer, a length and a handle where an
owned string has a pointer, a length and a capacity — Bytes’s shape, one
word wider than a bare view. Comparison and hashing are unchanged. A
value asserted &str is a pointer and a length, as now.
What the assertion buys is the door for the case where average is not good enough: a program that must not allocate says so once, in the declaration, and a later edit that would make it allocate is refused rather than quietly slower.
&[u8] follows. Bytes already behaves this way; the
assertion form for bytes is the same question and waits for a program.char sequence. chars() is a Seq[char]
(ADR-105); nothing here changes it.String stays because every neighbouring language has it;
a rename is a different decision and not owed here.D1 for literals, by ADR-207 (0.0.183), and not through step 2:
a literal where a String is wanted is constructed where the use keeps it and
handed over as it is where the use only reads it, which answers the allocation
per position instead of per representation. Step 3’s help and the
literal .to_string()s in examples/ came with it. What follows is what
stood before that, and is still true of every text value that is not a literal.
Nothing. String and &str are two types in the checker, the emitter and
std.contracts; a literal in a String slot is NK1106; no text carries a
handle. The order of work:
&str accepted where String is wanted, and String
accepted where &str is wanted with D2’s refusal where the value could
not be a borrowed view;std: text as Bytes is, and the state chosen by
the tether analysis of ADR-008;NK1106’s help and the thirteen .to_string() in examples/;--tethers listing text values.#200 carries it.