Generics and Trait Boundaries
uv’s internal crates use generics to specialize hot, strongly typed pipelines.
Preparer<'a, Context: BuildContext> borrows tags, cache, hashing policy, and a
DistributionDatabase parameterized by the build context. Resolver fetching is
generic over Provider: ResolverProvider. PEP 508 parsing is generic over a
Pep508Url representation. Associated types keep errors and domain values
connected to their provider.
Trait objects appear where optional runtime substitution is genuinely useful.
Option<Arc<dyn Reporter>> lets CLI progress implementations observe the same
pipeline without parameterizing every enclosing command. Errors are frequently
boxed at domain boundaries to keep large recursive error enums manageable.
Enums carry closed domain variation: Dist::Built | Source, cache buckets,
lock modes, link modes, and resolution outcomes. These are not trait objects
because exhaustive policy decisions are valuable.
The pattern is: generics for compile-time collaborators in core algorithms, enums for closed protocol/domain states, and trait objects for leaf observers or heterogeneous errors whose exact type no longer affects control flow.