ADR-305: The Language Below Is Rust 1.88 or Newer at Edition 2024 — One Measured Floor, for the Compiler, std and the Emitted Code Alike

Status: Accepted Date: October 3, 2026 History: the records this one replaces are archived in docs/history/adr; read them only for how the decision was reached Answers: the owner’s the minimum has to be in the manifests and the README, after a crate declared a newer Rust than the one the project promised; and the owner’s is the codebase compatible with Edition 2024’s stricter scoping at .await points, or do we need to change them? 1.94 as the new minimum would be fine Related: ADR-001 D1 — the channel is stable, and rust-toolchain.toml alone names it; ADR-288 D27 — the mechanism: one constant, RUST_FLOOR, written into every generated Cargo.toml and compared with rustc --version before cargo runs; ADR-277 D11 — the emitted impl AsyncFn and async closures, one of the features the floor has to cover; ADR-218 — the crates under crates/unsafe/, which carry the floor and the edition too; ADR-298 — a word the language below reserves is escaped where it is written

1. Question

Which Rust does Nikaia work with — the oldest version, and the edition — and how is that number kept true?

Nikaia’s compiler is written in Rust, its std is a Rust crate, and every program it compiles reaches cargo as Rust. All three meet one toolchain on a user’s machine, so the question has one answer for all of them. It cannot default: a minimum derived from one language feature and never checked afterwards is a promise nothing keeps, and the failure it produces — cargo refusing, or rustc failing on the generated file — is the class Part III C.1 forbids.

The edition is part of the same question, because it decides what the emitted code may write and when its values are dropped. Edition 2024 changes two drop orders, and a drop order matters at .await: a guard or borrow that lives past a pause is held for the whole pause and makes a future !Send.

2. How others do it

The field here is the language below’s own. Cargo reads a crate’s rust-version and refuses an older toolchain in its own words, and an edition is chosen per crate, so crates on different editions build together. The edition’s migration lints (cargo fix --edition) report each place whose meaning changes. Two of the changes concern drops:

Both make values live shorter, never longer. So a program that compiled under 2021 cannot start holding a lock across an .await because of them; what can change is an order of Drop that someone relied on.

3. Options, weighed

Option What it costs Verdict
A floor derived from one feature (-> impl Trait in a trait method, 1.75) and not checked goes stale without anyone noticing: the code came to need NonNull::map_addr (1.84), the emitted AsyncFn (1.85), std::io::pipe (1.87) and a compiler dependency that needs 1.88, while the documents still promised 1.75 rejected: a floor is measured, D2
A floor per component (compiler, std, emitted code) three numbers where one installation holds all three; the lowest promises what the others cannot keep rejected, D1
One floor, 1.88, measured in CI a job that runs the suite on an older toolchain chosen, D1-D3
Raise the floor to 1.94 with the edition switch locks out 1.88 through 1.93, and nothing measured needs them rejected, D7
Stay on Edition 2021 keeps the emitted code on an edition the language below has moved past, to avoid two changes in the emitter and none at a pause rejected, D5-D6
Edition 2024 for our crates and the emitted code; benchmark crates stay 2021 two emitter changes (unsafe extern, gen); no .await point changes chosen, D4-D6

The number that decided the floor: the compiler’s own build needs 1.88, the emitted code 1.85, and with 1.84 or 1.85 the generated file fails. The number that decided the edition: the migration lints over every crate of ours found no if_let_rescope site and ten tail_expr_drop_order sites, none of which holds anything across a pause.

4. Decision

D1 — The floor is 1.88, and it is one number for everything.

The compiler, std, the crates under crates/unsafe/, and the programs the compiler generates all require Rust 1.88 or newer. One number, because one installation holds all of them: the toolchain that builds Nikaia is the one that builds what it emits.

D2 — The floor is measured, and CI measures it again.

scripts/check-floor.sh reads the number from the emitter’s RUST_FLOOR, installs exactly that toolchain, and runs the whole test suite on it — which compiles every program the tests emit with it, against a std built with it — and the tests of every crate under crates/unsafe/. It also checks that every manifest says the same rust-version. CI runs it as a job of its own. A floor does not rise unless this passes on the new number; a feature that needs a newer Rust than the floor fails the job.

D3 — Where the floor is written.

rust-version = "1.88" in the manifest of every crate of the compiler and std (crates/nikaia, crates/nikaia-std, crates/orchestrator) and of every crate under crates/unsafe/; RUST_FLOOR in the emitter, which ADR-288 D27 writes into every generated Cargo.toml and compares with rustc --version before cargo runs; the getting-started instructions; Part III 13’s prerequisites; the comment in rust-toolchain.toml, which names the channel and not the floor (ADR-001 D1).

D4 — No .await point needs a change for Edition 2024.

The edition’s drop-order changes make temporaries live shorter. None of the places they touch holds a lock or an observed Drop across a pause, and the suite, which runs the runtime’s async paths, passes unchanged.

D5 — Our crates are Edition 2024.

This covers crates/nikaia, crates/nikaia-std, crates/orchestrator and every crate under crates/unsafe/. The benchmark crates under benches/ stay 2021: they are measurements, not the product, and an edition is per crate.

D6 — The emitted code is Edition 2024.

Every generated Cargo.toml, the grammar runner’s manifest and the tests’ rustc --edition say 2024. The emitter writes unsafe extern "C" { … }, which the edition requires. gen is a reserved word of the edition, so it joins the emitter’s list of words the language below reserves and a Nikaia name gen reaches Rust as r#gen (ADR-298); the sweep in tests/reserved_below.rs compiles it escaped in every position.

D7 — The edition does not raise the floor.

Edition 2024 needs Rust 1.85 and let chains need 1.88, which is already the floor (D1). Nothing the edition requires needs a newer Rust, so the floor stays 1.88 and D2’s job runs on it with everything on 2024.

5. Consequences

6. What this does not decide

This site is open source. Improve this page.