Snapshots, not seals¶
A feature registry that packages fill at init is process-wide by necessity: init can
reach nothing else, and a blank import is the whole point. The hazard is a reader that
enumerates while a writer can still register, so that two enumerations in one process
disagree.
The first answer, in go-tool-base, was a seal: reading the registry sealed it, and a registration afterwards panicked. It worked, and it made every root in a process share one sealed state, made a second root panic, and made every test that touched a feature reset the world first (184 times, in the end).
This package's answer is a snapshot. Registry.Snapshot() returns an immutable copy:
sorted descriptors, copied contributions. A reader resolves its Set from the snapshot and
is never surprised by a later registration, because a later registration is simply not in
its copy; a fresh snapshot sees it. Two readers with two snapshots disagree only if
something registered between them, which after main begins nothing does in a real binary.
Tests that register fakes do so on a registry of their own, so the default registry is a
function of the import graph and nothing else.
The one panic that remains is MustDeclare at init, where there is no caller to hand an
error to. Everything after main returns one.