ADR-222: A String Field or Result Is Below What Flows Into It — Text of Its Own, a View, or Either per Value

Status: Superseded by ADR-282 — its D1-D5 are D12-D13, D22, D14, D17 and D26 there Date: September 26, 2026 Target Version: Nikaia 0.0.203 Answers: #200’s question — a view kept where a String is declared: a copy or a tether? — and the README wall that came with it, with the owner’s rule for the answer: the costs are carried by the special case, not by the general one Refines: ADR-107 D1 (the cheapest state that works, chosen per use), which it builds for a struct’s fields and a function’s result; ADR-208 D2, whose refusal it narrows to one case (D2); ADR-208 §3’s first question, which it answers (D4) Related: ADR-209 (where a view’s buffer lives), ADR-221 (the same in a container that drops entries), ADR-207 D2 (a literal is built where it is kept), ADR-107 D3 (a copy is written, never inserted), ADR-037 D8 (a published representation is chosen before its users exist)

1. Context

ADR-107 D1 made String the one text type and its state the compiler’s. What was built was the literal half (ADR-207) and the half that only reads (ADR-208 D1). A view put where a String is kept stayed refused:

struct Person { name: String }

fn make(n: ref String) -> Person { return Person { name: n } }     // NK1106
fn first_word(line: ref String) -> String { return line.trim() }   // NK1104
fn main() { let n = "ada"; let p = Person { name: n } }            // NK1106

A fourth shape was worse than refused: Config { host: text.trim() } for a text the function read itself passed the checker and failed in rustc, about a file nobody wrote (Part III C.1).

#200 left open whether the answer is a copy or a tether. ADR-107 D3 had already ruled out the copy: one inserted by the compiler runs on every execution of the line with nothing in the source to show for it. So the answer is a view. What was open is what that does to a struct, which has one representation below, when some lines put views into a field and others put text of their own. Making the field a view everywhere would make every line with text of its own find a home for it, an allocation where it escapes. The general case would pay for the special one. The owner’s rule says it must not.

2. Decisions

D1 — Per field and per result, by what flows in: three tiers.

For every field declared String and every function declared -> String, the compiler reads what every line of the package puts there, before anything else looks at the program:

what flows in below who pays
text of its own only, the ordinary case String, unchanged nobody; nothing changes
views only (literals count as views of text that lives as long as the program) a view: ref String, as if the program had written it only what ADR-209 already charges a view — nothing where a frame outlives it, a handle where none does
both EitherText: a view is borrowed and text of its own is moved in, each at its own line the field alone: one branch on a read; no line pays for another’s kind of text

A literal alone moves nothing: a field only literals and text of its own flow into stays String, and the literal is built where it is kept, as ADR-207 D2 has it.

The tier is written into the declared type — ref String, or ref String marked either — so the checker, every derived column, the ledger and the emitter all read one answer. The view tier needs nothing new: the struct holds a view, and ADR-209 and ADR-221 decide where its buffer lives.

How a value’s kind is read. A literal is a view of static text; f"…", a .clone() and a String handed over are text of their own; a ref String parameter, a slice, and what a method of text cuts from its receiver (trim, lines, split, …) are views; a method or function of the program answers with its own declared or decided result. Anything this walk cannot read is text of its own, which is what the program was checked as before. A mistake in that direction leaves the old refusal, never a wrong program.

D2 — A published field or result may become a view, never both kinds.

A pub field of a pub struct, and a pub function’s result, leave with the package before the programs that use them exist, as ADR-037 D8 says of a published count. Where the package itself puts only views there, the ledger says ref String and every user reads that. A mixed representation would need a type no other package can know how to build. There the view is refused as before, with ADR-208 D2’s explanation, and that is the one case the refusal is still for.

D3 — Both kinds: nikaia_std::either_text::EitherText.

A small type in std, over Cow<str>: From a view borrows it, From a String moves it in, and a read derefs to str, prints, compares and hashes as its text. Its to_owned hands back a String, which is what the program’s .clone() of text is written as below. It is exactly as large as a String. Each value going into such a field, and each value such a function hands back, is wrapped where it is written. Nothing is copied: a view stays borrowed, and text of its own is moved in.

D4 — A name bound to a literal and kept as text of its own is declared String.

ADR-208 §3 left this open because it is inference across statements: the binding’s type decided by a later line. The walk that reads what flows into a field reads that too. Where let n = "ada" is kept in a position that stays text of its own, the binding is declared String, which is what the refusal used to ask the program to write. The literal is built once, where it is bound, as it would have been in place. Where the position is a view, nothing changes: the name stays a view of static text.

D5 — What was decided is reported.

nikaia --tethers lists every String field and result that is not text of its own below, and why, under text (declared String) — ADR-107 D4’s report. The ADR-209 lines below it say where each view’s buffer lives.

3. What is still refused, and what is not built

4. Consequences

The README’s wall put a view of text where a String is kept — a field, a return is gone for the ordinary program. #200 is closed. ADR-208 D2’s three explanations stay, for D2’s case and for kept arguments. Part III C.4’s example is a published field. A program that compiled before compiles to the same Rust: a position only text of its own flows into is the String it always was.

5. What is built

All of it, in 0.0.203:

This site is open source. Improve this page.