ADR-095: A Trait Reached Across a Package Is Imported by the Emitter

Status: Superseded by ADR-286 — its D1-D3 are D28-D30 there Date: September 15, 2026 Target Version: Nikaia 0.0.8 Related: ADR-078 (the trait declaration this makes reachable), ADR-046 D2 (use brings no names in — which is why the program cannot fix this itself), ADR-053 D1 (a package is its own crate, which is what makes the trait foreign), ADR-011 D2 (the emitter writes a name for a name, and here it had to write one nobody did)

1. Context

use http

impl http::Handler for Fixed {
    fn handle(&self) -> http::Response { … }
}

fn main() {
    print(http::render(&f.handle()))
}
error: no method named `handle` found for struct `Fixed` in the current scope
     = items from traits can only be used if the trait is in scope
     = trait `Handler` which provides `handle` is implemented but not in scope;
       perhaps you want to import it

Rust needs a trait in scope before its methods can be called, and impl http::Handler for Fixed does not put it there. So a trait a package publishes could be implemented and then not called.

That is Part III C.1’s class twice over. It is the backend’s words about a file nobody wrote, and the remedy the message names — “perhaps you want to import it” — is one the program cannot write: ADR-046 D2 decided that use brings no names in, so Nikaia has no import at all. A message asking for something the language does not have is worse than a message in the wrong words.

The same shape in one file compiles and runs, because there the trait is in the same module and needs no import. That is what said this was one missing emitted line rather than a question about the language.

2. Decisions

D1 — The emitter writes a use for every trait an impl in this unit names with a package in front of it.

Written as the path the program already wrote: impl http::Handler for Fixed names the crate http, and so does the import. No table, no resolution — the name is already the answer, which is ADR-011 D2’s rule applied to one more place rather than an exception to it.

D2 — Qualified is the whole of the test.

A trait of this program’s own is in the same module below and needs no import; a :: in the name is exactly what says the declaration is in another crate. A trait the compiler reads rather than a .nika file wrote — impl Error for ConfigError (ADR-023 D3) — carries no :: either, so the same test leaves it alone without needing to know about it.

D3 — allow(unused_imports), like the two lines above it.

A program may implement a trait and never call the method, and a warning about a line nobody wrote is Part III C.1 one severity down — which is ADR-090’s whole subject. The preamble’s two existing imports carry the attribute for the same reason.

3. Consequences

Needs gains a set and loses Copy, which is the only cost outside the one loop that writes the lines. The preamble grows by two lines per foreign trait an impl names, and by nothing at all for a program that names none — which is every program in the tree today.

#234’s defect closes — a trait a package publishes is implemented and then not callable.

4. What this does not decide

5. What is built

emit::foreign_traits, the Needs::foreign_traits set it fills, its union in Needs::join, and the loop in Needs::preamble. Exercised by a_trait_a_package_publishes_can_be_implemented_and_called in crates/nikaia/tests/project.rs — a real two-package project that builds and runs, printing what the handler returned. It has to be two packages: the one-file shape compiles either way, which is the fact that located the defect. Without the loop the test fails with the message in §1, quoted from the run.

This site is open source. Improve this page.