ref String field refuses the text (blocks a pull parser, #928)Open and answer this issue on GitHub.
wip · opened 2026-10-28 · updated 2026-10-29 · 4 comments
Checking what the yaml package (#928) needs from the language: a pull / event parser is a struct that holds the input and a position, and whose next returns views into the input. With target/fast/nikaia on develop (2026-10-28):
input: String, the text read at run timeuse std::fs
enum Value {
Str(String),
Seq(Vec[Value]),
}
struct Cursor {
input: String,
pos: i64,
}
fn next_item(mut c: Cursor) -> String? {
let rest = c.input.lines().skip(c.pos).collect()
if rest.len() == 0 {
return null
}
c.pos += 1
return rest[0].trim()
}
fn parse(input: String) -> Value {
let mut items: Vec[Value] = []
for l in input.lines() {
items.push(Value::Str(l.trim()))
}
return Value::Seq(items)
}
fn main() throws {
let text = fs::read_to_string("in.yaml", fs::Root::Anywhere)
let v = parse(text)
let mut c = Cursor { input: text, pos: 0 }
while true {
let s = next_item(c)
if s == null { break }
println(f"pull: {s}")
}
match v {
Value::Seq(items) => {
for it in items {
match it {
Value::Str(s) => println(s),
else => println("?"),
}
}
},
else => println("?"),
}
}
nikaia check accepts it; nikaia run stops in rustc:
error: Cannot move out of `text` because it is borrowed. --> 33: let mut c = Cursor { input: text, pos: 0 }
error: Lifetime may not live long enough. --> 19: return rest[0].trim()
Lowered: struct Cursor { input: String, .. } and fn next_item(c: &mut Cursor) -> Option<&'static str>. parse alone is fine (fn parse(input: &str) -> Value<'_>, enum Value<'a> { Str(&'a str), .. }): a zero-copy tree works; a struct that holds the text and hands out views of it does not.
Expected (ADR-353, ADR-283 D25): next_item’s result points into c; giving text to Cursor while v views it moves the buffer to the keep; either the program runs, or the checker refuses it with a Nikaia diagnostic. A program the checker accepts never reaches rustc’s errors.
input: ref Stringwarning[NK1261]: `ref` is redundant here: a field written `ref String` says what `String` already says.
error[NK1106]: `Cursor.input` holds `ref String`, but you're giving it `String`. --> Cursor { input: text, pos: 0 }
The warning says ref changes nothing; the error says it does. With a literal (let input = "…") the ref String form runs (Held pattern, examples/1brc.nika).
next_item returns points into c.input; Cursor lowers with a lifetime or the keep), or is refused before rustc with a diagnostic naming the view, as a test in tests/compiler/.ref String field takes a String local read at run time, as it takes a literal; or NK1261 no longer calls ref redundant where it changes what is accepted. Test beside 1.The commit that builds the last step says Fixes #N.
23336d8 (0.0.1261) builds both steps for the pull-parser shape:
mut struct parameter is a view of that parameter (rsrc_a_source, views.nika): fn next_item(mut c: Cursor) -> String? now has ref(c) in the ledger and lowers to -> Option<&str> instead of &'static str. Test a_mut_struct_parameter_hands_out_a_view_of_its_text.Held { input: text } with input: ref String lends text, as ref text does (check_core_places.nika), no more NK1106. Test text_given_to_a_ref_string_field_is_lent.Still open (why the issue stays open): probe 1 as written also gives text to Cursor while v = parse(text) still views it. That stops in rustc (Cannot move out of text because it is borrowed), not in the checker. ADR-353 says the buffer moves to the keep there; until that is built, the checker should at least refuse it with a Nikaia diagnostic. The same holds for let h = Held { input: text }; take(text); use(h) and its ref text spelling, which behaved the same before this change.
To build (left):
Generated by Claude Code
Found while trying a YAML line scanner as a grammar (#928), target/fast/nikaia at 0.0.1261:
grammar Lines {
rule WS = "" { }
rule Line -> (String, String) = ind:text(" "*) body:until(line_ending) line_ending { (ind, body) }
entry rule doc -> Vec[(String, String)] = ls:Line* eof { ls }
}
fn main() throws {
let text = "a:\n b: 1\n c: two\n"
for (ind, body) in Lines::doc(text) {
println(f"[{ind}] {body}")
}
}
error[NK1104]: This action builds `(ref String, ref String)`, but its rule returns `(String, String)`.
--> 3:90
= help: Make it a `(String, String)`, or change the declaration to `(ref String, ref String)`.
With (ref String, ref String) it runs and prints [] a:, [ ] b: 1, [ ] c: two, but every ref String then gets NK1261 (ref is redundant here … says what String already says). Where the spec says a String result means what ref String means (NK1261, ADR-282 D2), a rule declared -> (String, String) should take the views text(…) and until(…) yield, as a plain -> String rule does; the compiler holds it to the view.
To build (added):
String (in a tuple, a struct, a Vec) takes the views its action builds, as NK1261 says it means the same; test with the program above, no ref written.Generated by Claude Code
Stand an 0.0.1276:
next_item(c), weil der Aufruf nur c.pos ändert. Der Pull-Parser mit Parser { input: text } und ref mut self erreicht weiterhin rustc. Beides übernimmt #948.rule Line -> (String, String)), Probe unverändert.Generated by Claude Code
Schritt 4 ist auf develop (2f73c24, 0.0.1279). Ein Tupel aus String im Ergebnis einer Grammatikregel oder Funktion nimmt Views an, ohne ref. Struct, Enum, Vec und T? konnten das schon. Schritt 3 ist mit #948 im selben Commit erledigt. Der Commit sagt Fixes #948, #929.
Noch offen, aber nicht Teil dieses Issues: Wird ein solches Tupel in einem pub-Feld gehalten, kommt NK1104, und dessen Hilfe schlägt noch ref vor. Das widerspricht ADR-282 D2.
Generated by Claude Code