inventory

Typed distributed plugin registration for Rust with no central list required

Library
Cargo
v0.3.24
1,334stars
MIT OR Apache-2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
62/100Good
Development Activity56
Maintenance48
Community44
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
80/100Excellent
Architecture88
Code Quality84
Innovation85
Learning Curve65

inventory provides a way to set up a plugin registry that any source file linked into a Rust application can contribute to, without maintaining a single central list of registrations. A crate declares a plugin type once with inventory::collect!(Type), and then any crate — the declaring crate itself or any downstream dependent — can register instances with inventory::submit! { ... } from anywhere in its source, with all submissions across the whole binary becoming visible via inventory::iter::<Type> before main even runs.

This solves the same problem as tools like gflags’ distributed flag registration: a central list of registrations becomes a merge-conflict bottleneck once many contributors are adding to it independently. Under the hood, inventory relies on life-before-main constructor functions (similar to C’s __attribute__((constructor)) or the ctor crate) that run at program startup and dynamic-library load time, building a lock-free linked list per plugin type using atomic compare-exchange operations. It supports Linux, macOS, iOS, FreeBSD, Android, Windows, WebAssembly, and other platforms, with graceful degradation (no plugins found) elsewhere.

What You Get

  • inventory::collect!(Type) to declare a plugin registry for a type, called once in the crate that defines it
  • inventory::submit! { ... } to register a plugin instance from any crate with access to the type, at any point in the source
  • inventory::iter::<T> as a 'static-lifetime iterator over every registered instance of type T, ready to use at the start of main
  • Constructor-based registration (life-before-main) that works across dynamically loaded libraries too, registering at dlopen time
  • no_std support and WebAssembly compatibility, including guidance for ensuring constructors run correctly in Wasm’s command-style linkage model

Common Use Cases

  • Building a command-line flags library where any source file in a large application can register its own relevant flags
  • Implementing a plugin system where downstream crates contribute handlers, extensions, or test cases without editing a shared registry file
  • Collecting distributed test cases, benchmarks, or schema definitions scattered across many source files into one iterable collection
  • Avoiding merge-conflict-prone central registration lists in large, multi-contributor Rust codebases

Under The Hood

Architecture - Each collect!(Type) call generates a static REGISTRY: Registry holding an AtomicPtr<Node> used as the head of a lock-free singly linked list. Each submit! invocation expands (via the __do_submit! macro) into a static Node plus a platform-specific constructor function placed in a linker section that runs at program startup — .init_array on ELF platforms, __DATA,__mod_init_func on macOS/iOS, .CRT$XCU on Windows, with WebAssembly handled specially via __wasm_call_ctors and an initialized atomic-bool guard to prevent double-registration under Wasm’s fragile command-style-linkage heuristic. Registration itself uses compare_exchange-based CAS loops to prepend nodes to the list safely even if multiple constructors race (relevant for statically-linked large binaries). inventory::iter::<T> is implemented via a Deref-based zero-sized-type trick (credited to the ghost crate pattern) that lets a type alias double as both a type and a callable iterator constructor.

Tech Stack - #![no_std] pure Rust with zero mandatory dependencies; rustversion is pulled in only as a WebAssembly-target dependency to handle version-gated link_section attribute support across Rust toolchain versions. trybuild is used as a dev-dependency for macro-expansion/compile-fail testing.

Code Quality - tests/test.rs exercises multi-type registration and iteration correctness, while tests/compiletest.rs (via trybuild) verifies that misuse patterns — like calling submit! from inside a function body — fail to compile with a clear diagnostic, which is essential given how much of this crate’s correctness depends on macro-generated code that user mistakes could silently break. The crate documents unsafe invariants inline (e.g. SAFETY: requires *node.value is of type Self) at every unsafe block, and the WebAssembly-specific double-initialization guard shows the maintainer actively tracking edge cases in linker behavior across platforms, not just the common case.

API Design - The three-macro surface (collect!, submit!, iter::<T>) requires almost no boilerplate — plugin authors just write a submit! block anywhere in their crate, with no wiring back to a central file. The crate-level docs are unusually explicit about footguns (don’t call submit! inside a function body; iteration order is unspecified) and back that up with compile_fail doctests, reducing the risk of confused first-time usage of a fairly unusual (constructor-based) registration mechanism.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search