use, Everything Else Is Written With Its Module, and Each Spelling of the Other Is RefusedStatus: 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: which names a program may write with no use, why those and no
others, and what happens to a name written on the wrong side of that line;
#190 for the listed
names std did not have
Related:
ADR-286 D10-D14 (use makes a package reachable and brings no name in; a prefix must be introduced),
ADR-140 D5 (use brings nothing in for std as for a package, and a module nothing describes yet is left alone),
ADR-281 D1-D2 (which shape a shared value gets is the compiler’s; a hull is made by a call), D17-D19 (the doors over several locks, and their order) and D28 (taking a lock inside a lock is refused),
ADR-283 (Bytes is the language’s buffer),
ADR-293 D6 (panic is on the list),
ADR-269 D2 (assert is a prelude function the compiler knows),
ADR-119 (a target with no operating system),
Part I 1.3, 2.2, 6.2 and 9.1, Part II 12.2-12.4, Part III 17.1 and C.4
Which names need no use, and what decides whether a name is one of them?
use brings no name in, for std as for a package (ADR-286,
ADR-140 D5). So every name a program writes bare is a promise
that is very hard to take back: removing one breaks every program that wrote
it. Left undecided, the line is drawn by whatever the compiler happens to know,
which grows one name at a time as something is needed - and then a program can
read a file without saying so, and a reader cannot find out what needs no use
without reading the compiler.
The question has two sides that a single answer has to cover: which names are on the list, and what a program meets when it writes a name on the wrong side of it - a module name without its prefix, or a listed name with one.
No other language is weighed; the comparison is with a list nobody writes
down - a Rust prelude module in nikaia-std whose pub use lines are the
list. Its cost is the reason for D1: what such a list carries is not argued
for, and nothing says when it is complete.
What does not transfer from a host language’s prelude is its reason for holding a name. A name here is on the list because the language decides what it does (D4-D6), not because it is common; a prelude that grows by common enough has no floor (D3).
| Option | Example | Why it lost / won |
|---|---|---|
No written list: whatever std exports bare |
let t = fs::read_to_string(…) with no use |
lost: the list is a property of a Rust file, it grows by need, and it lets a program do I/O without saying so |
| A written list, built from a rule | use std::fs then fs::read_to_string(…) |
chosen (D1-D2): a reader reads one page; a name costs a record |
HashMap on the list, because a map is common |
let m = HashMap() |
lost (D3): common is not a bar, and use std::collections is one line |
eprint behind use std::io |
io::eprint("…") |
lost (D4): it would say a module decides what it does, and only the compiler knows the target |
access_all / update_all behind use std::lock |
lock::access_all(a, b) … |
lost (D5): it reads as an alternative to nesting access, which is the reading that makes a deadlock possible |
| The shared types in a module | lock::SharedMut(0) |
lost (D6): the compiler picks their representation per value; no module owns them |
| A per-project prelude | a manifest key | not taken (§6): one program’s Vec becomes another’s mystery |
A listed name written with a module left to rustc |
io::println("x") |
lost (D9): the reader’s consistent guess ends in an error about a file nobody wrote, and a lock spelt this way types as nothing, so the nesting refusal does not run |
| Refusing every unknown module prefix | use std::db::postgres |
lost (D9): it would refuse a module nothing describes yet, which ADR-140 D5 leaves alone for Part III C.4’s reason |
Vec, String,
Bytes;println, print, eprintln,
eprint;assert and panic;access_all and update_all;Shared, SharedMut and Locked;The list is closed: a name not on it is reached through its module, after
use std::<module>, the way a package is reached (Part I 9.1). D4-D6 say why
the later entries are on it.
That is the rule the list is built from, not a property it happens to have.
Reading a file is something a program should say it does, and use std::fs is
that sentence. It is also what makes the list safe on a target with no
filesystem (ADR-119): it does not have to be cut down for one,
so it does not differ by target. By the same rule sleep, which pauses, is
time::sleep (Part II 12.4).
HashMap is the first thing outside it.A map is common enough to argue for, and that is why it is the line: a list
that grows by common enough has no floor. A program writes
collections::HashMap after use std::collections, and the other collections
and std’s other types live in their modules too, with their methods.
eprint is on the list, because diagnosis is the language’s.It is what a program says when something has gone wrong, which is what
panic (ADR-293 D6) and assert beside it are for. What it does
is not one thing: a file descriptor on a hosted target, a call into the host on
WebAssembly, a serial line or nothing at all with no operating system. A module
does not know the target, and the compiler does; use std::io in front of it
would say it is a library function like any other, which is the one thing it is
not. eprintln is on the list for the same reason.
access_all and update_all are on the list, because safety is the language’s.They are the doors over several locks (Part II 12.3). Every acquisition takes
the locks in one order however the program wrote them
(ADR-281 D19), so access_all(a, b) in one task and
access_all(b, a) in another take them alike and a deadlock cycle cannot form.
That is a property of the language rather than a convenience. They are free
functions only because a method has one receiver and these have several; behind
a module they would read as an alternative to nesting access, which is
exactly the reading that makes the deadlock possible.
Shared, SharedMut and Locked are on the list, because the compiler picks their representation.Which shape a shared value gets is decided per value by the compiler
(ADR-281 D1), and a type whose representation the compiler picks
is not one a module owns - the argument ADR-283 makes for
Bytes, one type over. Part I 6.2 defines them in the language chapter, a
program makes one with a call and no import (SharedMut(0),
ADR-281 D2), and Part II 12.3’s nesting rule is the language’s.
The names that are reachable and stay off the list are the ones no program
writes - Entry, TaskHandle, Args: each arrives as a result and none can
be constructed.
Adding is the easy direction, and it is the one that ends with everything on the list. A name on it is argued for in a record of its own, with D2 as the bar and D4-D6 as the kinds of reason that have passed it.
The generated Rust imports what the lowering needs - the runtime so the
fn main the emitter writes can start it, a map’s hasher, the modules whose
prefix the source wrote. Those are not names a .nika file may write. The two
lists answer different questions - what a program may name, and what the
generated file needs to compile - and they are allowed to differ.
std name that lives in a module, written without its prefix, is refused:
NK1117 for a function, NK1135 for a type. A prefix used without its use
is refused too. Each says the line to add.std module in front of it - io::println -
is refused (NK1163). This is the direction a reader actually goes wrong in, because every other std name
wants its module. Both halves are required: the module genuinely has no such
name, and the bare one is a name std keys. So it cannot refuse a correct
program: what it refuses compiles under no reading. A prefix that is not a
module std has is left where ADR-140 D5 leaves a module
nothing describes yet.The direction a test must watch is a name that is reachable and unlisted,
because nobody meets it: nothing refuses it. So a test reads Part I 1.3 and
asserts that every function std keys bare is named there, and that every type
the checker knows by name (the hulls) is named there. The other direction - a
name listed and missing - is one a program meets on its own, at its first use.
use by reading one page, and Part I 1.3
and Part III 17.1 say the same thing about the printing functions.use.rustc about generated code, and never a lock
silently typed as nothing that escapes the nesting refusal.use std::… line in front of fs, io, time,
collections and the rest wherever a program reaches them; one record for
every name that joins the list; and two lists kept apart, the program’s and
the emitter’s.eprint does on a target with no console. D4 says the answer is
the compiler’s, not what it is; Part III 17.1’s availability table is where
it belongs, and wasm32-* is the first target to need it.lock::SharedMut(0) where lock is no module of std, which nothing
refuses: ADR-140 D5’s deliberate silence about a surface that
does not exist yet.Entry, TaskHandle and Args should be keyed under a module.
Nobody writes them, so nothing is wrong either way.