Generics, Trait Objects, and Compiler Families
The public binary has a dynamic problem: at runtime an executable may be rustc,
Clang, GCC, MSVC, CUDA, or a proxy. Compiler<C>, CompilerProxy<C>,
CompilerHasher<C>, and Storage are therefore trait-object boundaries.
Collections can hold heterogeneous implementations and configuration can pick
disk, Redis, S3, or multi-level storage.
The command creator C remains generic. Production uses real processes; tests
substitute a deterministic mock without adding dynamic dispatch to every
method. Associated behavior is erased only where heterogeneity is required.
Compiler::parse_arguments separates cheap compiler-family recognition from a
per-invocation CompilerHasher. That hasher owns parsed arguments and can
perform async dependency discovery, key generation, lookup, and fallback
compilation.
CompileResult is an enum because callers must exhaustively distinguish hit,
miss, uncacheable input, compiler failure, and infrastructure error. Its cache
miss variant owns a boxed future for cache publication: only that future’s
concrete implementation is irrelevant to the response layer.
The pattern is:
Generics preserve testable families; trait objects represent runtime plugin choices; enums preserve a closed set of product outcomes.