Generics and Tower Stacks
Linkerd is an unusually strong example of generics as architecture.
Service<Req> associates a response, error, and future with one request type.
NewService<T> associates a constructed service with one target type. Layer<S>
maps an inner service type to a wrapped service type. Param<P> proves a target
can supply a layer’s configuration. Together, they make invalid stack wiring a
compile error.
Builder methods return impl Layer<..., Service = ...> and each .push()
changes the enclosing Inbound<S>/Outbound<S> type. The enormous final type
is optimized into direct calls. Enums such as Either<A, B> retain static
dispatch for closed runtime branches.
Type erasure is applied at pressure points: buffered services need a uniform
queued request type; recursive/dynamic routing needs cached heterogeneous
boundaries; public application structs sometimes hide futures. BoxService,
BoxCloneSyncService, boxed bodies, and the shared linkerd_error::Error stop
type growth where flexibility outweighs inlining.
The design does not choose “generics or trait objects.” It composes statically for the hot path, then erases at queues, recursive boundaries, and application storage.