Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Generics and Tower

Axum’s generics allow independently written handlers, extractors, bodies, listeners, middleware, and runtimes to agree on contracts.

Service<Request> is the common language

Tower’s conceptual interface is:

trait Service<Request> {
    type Response;
    type Error;
    type Future: Future<Output = Result<Self::Response, Self::Error>>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>;
    fn call(&mut self, request: Request) -> Self::Future;
}

Layers transform one generic service type into another. Axum can therefore reuse timeouts, tracing, limits, and authorization without owning them.

How an async function implements Handler

Every async fn has an anonymous future type, and every argument list gives a different function type. Axum implements Handler<T, S> for functions whose arguments implement extraction traits, whose future is Send, and whose output implements IntoResponse.

The T parameter encodes the argument tuple and marker type. It is partly a coherence workaround that distinguishes blanket implementations which might otherwise overlap.

Source: handler/mod.rs:140

A macro expands tuple arities, not runtime magic

impl_handler! generates the same implementation for supported argument counts. At runtime it is ordinary code: split request, await each extractor, call the function, convert the result. The macro compensates for the lack of variadic generics.

Two extractor traits encode body ownership

FromRequestParts<S> receives mutable metadata and a shared state reference. FromRequest<S> owns the complete request. All but the final handler argument must use parts; the last may consume the body. A resource constraint becomes a generic bound.

State is a missing type

Router<S> means “a router still missing state S,” not “a router currently holding S.” Calling with_state supplies that value and can yield Router<()>, which is serveable. State<Inner> uses FromRef<Outer> to derive focused substate from the application’s outer state.

This catches incomplete wiring at compile time while letting libraries request only the state they need.

Static and dynamic dispatch meet in the middle

The public builder API retains concrete generic types for checking and composition. Internally, routes use a cloneable boxed service to store heterogeneous endpoints together. Good generic design chooses the boundary where type erasure makes the whole system usable.