Rust 1.99 compatibility and selective adoption
Canonical Quirl project documentation synced from docs/decisions/2026-10-04_162535286_rust-1-99-compatibility-and-selective-adoption.md.
Accepted · 2026-10-04
- Baseline: Rust 1.99.0, Edition 2024, commit
b940084d7(2026-09-28)
Context
This audit continues the Rust 1.97 record, which pinned
1.97.1 and adopted only changes that made a bound, parser transition,
diagnostic, or invariant clearer. Its adopted decisions (array_windows::<2>
for two-token parser transitions, assert_matches! in tests, and the
dead_code_pub_in_binary deny) remain in force. The audit covers Rust,
Cargo, and Clippy 1.98.0, 1.98.1, and 1.99.0 using the official
Rust release notes.
Release audit
| Release | Quirl-relevant finding | Disposition |
|---|---|---|
| 1.98.0 | bool::ok_or_else, str::strip_circumfix, String::from_utf16le, and Send/Sync for CommandArgs stabilized. std::env::Vars is no longer Send/Sync; assert_eq! gained a temporary scope; derive(PartialOrd) takes an Ord fast path. | Validate: Quirl captures the environment into owned maps before crossing threads, and every PartialOrd derive is consistent with its Ord. No new API improves an existing invariant enough to adopt. |
| 1.98.1 | Fixed a miscompilation when generating vtables. | Required. Quirl routes completion, extension, picker, and terminal providers through trait objects, so the pin must include this fix. |
| 1.99.0 | String::from_utf8_lossy_owned stabilized; atomic fetch_update is deprecated in favor of try_update; LLVM 23; legacy integral modules fully deprecated. Clippy added map_or_identity, assert_is_empty (pedantic), a chunks_exact-to-as_chunks suggestion, and stricter branches_sharing_code. | Adopt from_utf8_lossy_owned where captured process output is decoded, rename fetch_update to try_update, decode embeddings with as_chunks::<4>, and fix the map_or_identity and shared-tail findings. Allow assert_is_empty. |
Decision
Pin Rust 1.99.0 in rust-toolchain.toml, the workspace rust-version,
and the Linux check image (by digest).
Decode captured stdout and stderr with String::from_utf8_lossy_owned. The
capture already owns its bytes, and the previous
from_utf8_lossy(&bytes).into_owned() copied up to 1 MiB per stream even
when the output was valid UTF-8.
Decode embedding vectors with as_chunks::<4>(). The fixed-size chunks
replace a fallible try_into whose fallback silently produced zero bytes;
the existing length check guarantees an empty remainder.
Allow clippy::assert_is_empty workspace-wide. Its suggested replacement,
assert_ne!(value, [] as [T; 0]), is harder to read than
assert!(!value.is_empty()) across 114 test assertions, and tests that need
the value on failure already print it explicitly.
Do not adopt bool::ok_or_else or str::strip_circumfix broadly: the
existing early returns name their errors at the decision point, and quote
handling already validates both delimiters separately.
Consequences
- The MSRV is exactly 1.99.0.
- Captured output no longer pays a full copy on the common valid-UTF-8 path.
- The vendored
vt100copy usesu8::MAXinstead of the deprecatedu8::max_value(). - Future compiler upgrades repeat this audit.
Non-interactive invocations and shell setup code run in POSIX sh
Canonical Quirl project documentation synced from docs/decisions/2026-10-04_044214653_non-interactive-invocations-and-shell-setup-code-run-in-posi.md.
Contributing
Work on Quirl with the same safety, performance, and quality boundaries as the project.