Today — Screen::row_text returns an empty string for a row that does not exist, and says so in its own rustdoc. Measured against 0.9.0 on a 24-row screen:
let s = t.screen();
s.row_text(99) // "" — no row 99; indistinguishable from a blank row
s.cell(99, 0) // None — the same question, answered
s.rect_text(0.., 99..) // "" — clamped, deliberately
Three neighbouring accessors, three different answers to "you asked for something outside the grid".
Why it is worth fixing — the crate has already decided this question once, in the opposite direction, and wrote the reasoning down. rect_text panics on a backwards range, and its rustdoc explains why at length (screen.rs, the "Panics" section):
A panic rather than an error, deliberately, and for the same reason &slice[3..0] panics: a backwards range is not a fact about the terminal discovered at runtime, it is a mistake in the calling source. Returned quietly it read as "" — "this pane is empty", a perfectly plausible assertion outcome — so a mis-ordered call passed for the wrong reason and kept passing.
That paragraph describes row_text(99) exactly. assert_eq!(s.row_text(30).trim(), "") on a 24-row screen passes, and passes for the wrong reason, and keeps passing after the application stops drawing anything at all. An off-by-one over rows(), a hardcoded row index that outlived a size() change, or a (col, row) pair passed row-first are all the same mistake in the calling source, and all of them are answered "".
The documented behaviour is not a defence: it is documented as a fact, without the reasoning rect_text gives for the opposite choice, and a reader who has read rect_text would reasonably expect the other answer.
Fix — pick one of the two answers the crate already gives, and make the trio consistent. In order of preference:
- Panic, like
rect_text, with a message in the same shape: row_text: row 99 is outside the 24-row screen. It is the same class of mistake and gets the same treatment. Breaking only for callers who were relying on the quiet "", which is the bug being fixed.
- Return
Option<String>, like cell. More honest still, and the most disruptive: every call site grows an .unwrap() or a ?, including the ones in this repository's own suite.
Whichever is chosen, text() and rect_text must keep working — text() iterates 0..self.rows so it never asks out of range, and rect_text clamps before it reads. Note the distinction rect_text draws and keep it: an out-of-range bound is clamped because "asking for more screen than exists is a reasonable thing to do", while an inverted range is a mistake. A single row index has no range to clamp, so it falls on the mistake side.
Search the suite for row_text( before changing anything: the calls there all index within the grid, so the migration should be small, and any that do not are themselves the bug this finds.
Done when — row_text no longer answers an out-of-range row with ""; its rustdoc carries the reasoning rather than only the behaviour, and cross-references cell and rect_text so the trio reads as one decision; a test covers the out-of-range call; and text() and rect_text are unchanged.
Today —
Screen::row_textreturns an empty string for a row that does not exist, and says so in its own rustdoc. Measured against 0.9.0 on a 24-row screen:Three neighbouring accessors, three different answers to "you asked for something outside the grid".
Why it is worth fixing — the crate has already decided this question once, in the opposite direction, and wrote the reasoning down.
rect_textpanics on a backwards range, and its rustdoc explains why at length (screen.rs, the "Panics" section):That paragraph describes
row_text(99)exactly.assert_eq!(s.row_text(30).trim(), "")on a 24-row screen passes, and passes for the wrong reason, and keeps passing after the application stops drawing anything at all. An off-by-one overrows(), a hardcoded row index that outlived asize()change, or a(col, row)pair passed row-first are all the same mistake in the calling source, and all of them are answered"".The documented behaviour is not a defence: it is documented as a fact, without the reasoning
rect_textgives for the opposite choice, and a reader who has readrect_textwould reasonably expect the other answer.Fix — pick one of the two answers the crate already gives, and make the trio consistent. In order of preference:
rect_text, with a message in the same shape:row_text: row 99 is outside the 24-row screen. It is the same class of mistake and gets the same treatment. Breaking only for callers who were relying on the quiet"", which is the bug being fixed.Option<String>, likecell. More honest still, and the most disruptive: every call site grows an.unwrap()or a?, including the ones in this repository's own suite.Whichever is chosen,
text()andrect_textmust keep working —text()iterates0..self.rowsso it never asks out of range, andrect_textclamps before it reads. Note the distinctionrect_textdraws and keep it: an out-of-range bound is clamped because "asking for more screen than exists is a reasonable thing to do", while an inverted range is a mistake. A single row index has no range to clamp, so it falls on the mistake side.Search the suite for
row_text(before changing anything: the calls there all index within the grid, so the migration should be small, and any that do not are themselves the bug this finds.Done when —
row_textno longer answers an out-of-range row with""; its rustdoc carries the reasoning rather than only the behaviour, and cross-referencescellandrect_textso the trio reads as one decision; a test covers the out-of-range call; andtext()andrect_textare unchanged.