What is the role of tower::Layer trait in middleware implementation and how does it differ from tower::Service?
tower::Layer is a trait that defines middleware as a transformation from one Service to another, while tower::Service defines the actual request-handling behavior. The key distinction is that Layer creates middleware, Service is middlewareâLayer::layer() takes an inner service and returns a wrapped service that adds behavior before or after delegating to the inner service. This separation allows composing middleware declaratively: each layer wraps the next, forming a stack where requests flow through each layer in order. The Layer trait exists because middleware needs to be instantiated per-service, and the trait provides a uniform interface for that instantiation regardless of what the middleware actually does.
The Service Trait
use tower::Service;
use std::task::{Context, Poll};
use std::future::Future;
use std::pin::Pin;
// Service is the core abstraction: handle a request, return a response
struct MyService;
impl Service<String> for MyService {
type Response = String;
type Error = std::convert::Infallible;
type Future = std::future::Ready<Result<String, std::convert::Infallible>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: String) -> Self::Future {
std::future::ready(Ok(format!("Received: {}", req)))
}
}Service handles requests. Every service implements call() to process requests and return responses.
Basic Layer Trait
use tower::Service;
use std::task::{Context, Poll};
// Layer transforms one Service into another
trait Layer<S> {
type Service: Service<Request>;
fn layer(&self, inner: S) -> Self::Service;
}
// A simple logging layer
struct LoggingLayer;
impl<S> Layer<S> for LoggingLayer
where
S: Service<String>,
{
type Service = LoggingService<S>;
fn layer(&self, inner: S) -> Self::Service {
LoggingService { inner }
}
}
// The wrapped service that adds logging behavior
struct LoggingService<S> {
inner: S,
}
impl<S> Service<String> for LoggingService<S>
where
S: Service<String>,
{
type Response = S::Response;
type Error = S::Error;
type Future = LoggingFuture<S::Future>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: String) -> Self::Future {
println!("Request: {}", req);
LoggingFuture {
inner: self.inner.call(req),
}
}
}Layer::layer() wraps an inner service, returning a new service that adds behavior.
Using Tower's Layer Trait
use tower::ServiceBuilder;
use tower::layer::Layer;
use tower::Service;
use std::time::Duration;
// Tower provides ServiceBuilder for composing layers
fn example() {
let service = ServiceBuilder::new()
.layer(tower::layer::timeout(Duration::from_secs(5)))
.layer(tower::layer::load_shed())
.layer(tower::layer::limit(100, 1000))
.service(MyService);
}
struct MyService;
impl Service<String> for MyService {
type Response = String;
type Error = std::convert::Infallible;
type Future = std::future::Ready<Result<String, std::convert::Infallible>>;
fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
std::task::Poll::Ready(Ok(()))
}
fn call(&mut self, req: String) -> Self::Future {
std::future::ready(Ok(format!("Handled: {}", req)))
}
}ServiceBuilder::layer() chains layers to create a service stack.
Layer Creates, Service Handles
use tower::layer::Layer;
use tower::Service;
use std::task::{Context, Poll};
// Layer: factory that creates middleware services
// Service: the middleware itself that handles requests
// Example: Rate limiting layer
struct RateLimitLayer {
max_requests: usize,
}
impl<S> Layer<S> for RateLimitLayer
where
S: Service<String>,
{
type Service = RateLimitService<S>;
fn layer(&self, inner: S) -> Self::Service {
RateLimitService {
inner,
remaining: self.max_requests,
}
}
}
// The actual service that does rate limiting
struct RateLimitService<S> {
inner: S,
remaining: usize,
}
impl<S> Service<String> for RateLimitService<S>
where
S: Service<String>,
{
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
if self.remaining == 0 {
// Rate limited - not ready for more requests
return Poll::Pending;
}
self.inner.poll_ready(cx)
}
fn call(&mut self, req: String) -> Self::Future {
self.remaining -= 1;
self.inner.call(req)
}
}Layer creates the middleware instance; Service handles each request.
Layer Composition
use tower::ServiceBuilder;
use tower::layer::Layer;
use tower::Service;
use std::time::Duration;
fn compose_layers() {
// Layers compose from outside to inside
// First layer is outermost, last is innermost
let stack = ServiceBuilder::new()
.layer(LoggingLayer) // Outer: logs all requests
.layer(RetryLayer) // Middle: retries on failure
.layer(TimeoutLayer) // Inner: enforces timeout
.service(InnerService); // Core: handles business logic
// Request flow:
// Client -> Logging -> Retry -> Timeout -> InnerService
// Response flow (reverse):
// InnerService -> Timeout -> Retry -> Logging -> Client
}
struct LoggingLayer;
struct RetryLayer;
struct TimeoutLayer;
struct InnerService;
impl<S> Layer<S> for LoggingLayer {
type Service = LoggingService<S>;
fn layer(&self, inner: S) -> Self::Service { LoggingService { inner } }
}
// ... similar implementations for RetryLayer and TimeoutLayerLayers wrap from outside to inside; requests flow through in order.
Stateful vs Stateless Layers
use tower::layer::Layer;
use tower::Service;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
// Stateless layer: configuration only, no per-request state
struct TimeoutLayer {
duration: std::time::Duration,
}
impl<S> Layer<S> for TimeoutLayer
where
S: Service<String>,
{
type Service = TimeoutService<S>;
fn layer(&self, inner: S) -> Self::Service {
TimeoutService {
inner,
duration: self.duration,
}
}
}
// The service holds the per-connection state
struct TimeoutService<S> {
inner: S,
duration: std::time::Duration,
}
// Stateful layer: might need to share state between services
struct CounterLayer {
counter: Arc<AtomicUsize>,
}
impl<S> Layer<S> for CounterLayer
where
S: Service<String>,
{
type Service = CounterService<S>;
fn layer(&self, inner: S) -> Self::Service {
CounterService {
inner,
counter: Arc::clone(&self.counter),
}
}
}
struct CounterService<S> {
inner: S,
counter: Arc<AtomicUsize>,
}
impl<S> Service<String> for CounterService<S>
where
S: Service<String>,
{
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: String) -> Self::Future {
self.counter.fetch_add(1, Ordering::SeqCst);
self.inner.call(req)
}
}Layers configure services; services hold per-request state.
Why Layer Exists
use tower::layer::Layer;
use tower::Service;
// Without Layer trait: manual creation for each service type
fn without_layer() {
// Would need to manually instantiate for each inner service type
let _logging_over_timeout = LoggingService {
inner: TimeoutService {
inner: InnerService,
duration: std::time::Duration::from_secs(5),
},
};
// Can't easily compose with different inner services
}
// With Layer trait: generic composition
fn with_layer() {
let timeout_layer = TimeoutLayer { duration: std::time::Duration::from_secs(5) };
let logging_layer = LoggingLayer;
// Works with any inner service
let service = logging_layer.layer(timeout_layer.layer(InnerService));
// Or compose multiple layers
let layered = ServiceBuilder::new()
.layer(logging_layer)
.layer(timeout_layer)
.service(InnerService);
}
struct TimeoutLayer { duration: std::time::Duration }
struct LoggingLayer;
struct InnerService;
// ... implementationsLayer enables generic middleware composition without knowing inner types.
Async Middleware with Layer
use tower::layer::Layer;
use tower::Service;
use std::task::{Context, Poll};
use std::pin::Pin;
use std::future::Future;
struct AsyncLoggingLayer;
impl<S> Layer<S> for AsyncLoggingLayer
where
S: Service<String>,
{
type Service = AsyncLoggingService<S>;
fn layer(&self, inner: S) -> Self::Service {
AsyncLoggingService { inner }
}
}
struct AsyncLoggingService<S> {
inner: S,
}
impl<S> Service<String> for AsyncLoggingService<S>
where
S: Service<String>,
{
type Response = S::Response;
type Error = S::Error;
type Future = LoggingFuture<S::Future>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: String) -> Self::Future {
println!("Request started: {}", req);
LoggingFuture {
inner: self.inner.call(req),
}
}
}
// Future that logs when complete
struct LoggingFuture<F> {
inner: F,
}
impl<F, T, E> Future for LoggingFuture<F>
where
F: Future<Output = Result<T, E>>,
{
type Output = Result<T, E>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = unsafe { self.get_unchecked_mut() };
let inner = unsafe { Pin::new_unchecked(&mut this.inner) };
match inner.poll(cx) {
Poll::Ready(result) => {
println!("Request completed");
Poll::Ready(result)
}
Poll::Pending => Poll::Pending,
}
}
}Layer implementations often need to wrap futures for async behavior.
Layer in Axum Middleware
use axum::{
Router,
routing::get,
Extension,
};
use tower::ServiceBuilder;
use std::time::Duration;
async fn handler() -> &'static str {
"Hello"
}
#[tokio::main]
async fn main() {
// Layer is used to add middleware to Axum
let app = Router::new()
.route("/", get(handler))
.layer(
ServiceBuilder::new()
.layer(tower_http::trace::TraceLayer::new_for_http())
.layer(tower::limit::RateLimitLayer::new(100, Duration::from_secs(1)))
.layer(tower::timeout::TimeoutLayer::new(Duration::from_secs(10)))
);
// Each layer wraps the next:
// TraceLayer wraps RateLimitLayer wraps TimeoutLayer wraps handler
}
// Layers can also be added individually
fn individual_layers() -> Router {
Router::new()
.route("/", get(handler))
.layer(tower::timeout::TimeoutLayer::new(Duration::from_secs(10)))
.layer(tower::limit::RateLimitLayer::new(100, Duration::from_secs(1)))
// Order matters: layers added first wrap later layers
}Axum uses Layer to compose middleware stacks.
Clone Requirement
use tower::layer::Layer;
use tower::Service;
use std::task::{Context, Poll};
// Layer must be Clone to be used in ServiceBuilder
#[derive(Clone)]
struct MyLayer {
config: String,
}
impl<S> Layer<S> for MyLayer
where
S: Service<String> + Clone,
{
type Service = MyService<S>;
fn layer(&self, inner: S) -> Self::Service {
MyService {
inner,
config: self.config.clone(),
}
}
}
struct MyService<S> {
inner: S,
config: String,
}
impl<S> Service<String> for MyService<S>
where
S: Service<String>,
{
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: String) -> Self::Future {
println!("Config: {}", self.config);
self.inner.call(req)
}
}Layers are typically Clone to support service creation.
LayerFactory for Dynamic Layers
use tower::layer::Layer;
use tower::Service;
use std::sync::Arc;
// Some layers need state that's created per-service
struct StatefulLayer<T> {
make_state: Arc<dyn Fn() -> T + Send + Sync>,
}
impl<S, T> Layer<S> for StatefulLayer<T>
where
S: Service<String>,
T: Clone,
{
type Service = StatefulService<S, T>;
fn layer(&self, inner: S) -> Self::Service {
StatefulService {
inner,
state: (self.make_state)(),
}
}
}
struct StatefulService<S, T> {
inner: S,
state: T,
}
// Example: per-service counter
fn create_counter_layer() -> impl Layer<MyService> {
let counter = Arc::new(atomic_counter::AtomicCounter::new(0));
move |service: MyService| {
CounterService {
inner: service,
counter: counter.clone(),
}
}
}
mod atomic_counter {
use std::sync::atomic::{AtomicUsize, Ordering};
pub struct AtomicCounter(AtomicUsize);
impl AtomicCounter {
pub fn new(val: usize) -> Self {
Self(AtomicUsize::new(val))
}
pub fn inc(&self) -> usize {
self.0.fetch_add(1, Ordering::SeqCst)
}
}
}
struct MyService;
struct CounterService<S> { inner: S, counter: Arc<atomic_counter::AtomicCounter> }Layers can use closures to create per-service state.
Built-in Tower Layers
use tower::ServiceBuilder;
use std::time::Duration;
fn built_in_layers() {
let _service = ServiceBuilder::new()
// Timeout layer
.layer(tower::timeout::TimeoutLayer::new(Duration::from_secs(5)))
// Rate limiting
.layer(tower::limit::RateLimitLayer::new(100, Duration::from_secs(1)))
// Load shedding (reject when overloaded)
.layer(tower::load_shed::LoadShedLayer::new())
// Retry failed requests
.layer(tower::retry::RetryLayer::new(MyRetryPolicy))
// Buffer requests
.layer(tower::buffer::BufferLayer::new(100))
// Service is created at the end
.service(MyService);
// Each layer transforms the service
}
struct MyService;
struct MyRetryPolicy;
// ... implementationsTower provides many standard layers for common middleware patterns.
Layer vs Service Summary
use tower::layer::Layer;
use tower::Service;
// Layer: Factory pattern
// - Creates middleware services
// - Called once per service instantiation
// - Contains configuration, not runtime state
// - Returns a Service
// Service: Strategy pattern
// - Handles requests
// - Called many times per service
// - Contains per-request state
// - Returns a Future
trait LayerComparison<S> {
type Service: Service<String>;
// Layer::layer is called ONCE to create service
fn layer(&self, inner: S) -> Self::Service;
}
trait ServiceComparison {
type Response;
type Error;
type Future: Future<Output = Result<Self::Response, Self::Error>>;
// Service::call is called MANY TIMES for requests
fn call(&mut self, request: String) -> Self::Future;
}
use std::future::Future;Layer::layer() is called once; Service::call() is called per request.
Identity Layer
use tower::layer::Layer;
use tower::Service;
// Identity layer passes through unchanged
struct IdentityLayer;
impl<S> Layer<S> for IdentityLayer {
type Service = S;
fn layer(&self, inner: S) -> Self::Service {
inner // No wrapping, returns as-is
}
}
// Useful as a default or placeholder
fn optional_layer<S>(enable: bool) -> impl Layer<S>
where
S: Service<String> + Clone,
{
if enable {
// Wrapping layer
tower::util::Either::A(TimeoutLayer { duration: std::time::Duration::from_secs(5) })
} else {
// Identity - no wrapping
tower::util::Either::B(IdentityLayer)
}
}
struct TimeoutLayer { duration: std::time::Duration }IdentityLayer is useful for conditional middleware.
Real-World Example: Metrics Layer
use tower::layer::Layer;
use tower::Service;
use std::task::{Context, Poll};
use std::sync::Arc;
use std::time::Instant;
use std::pin::Pin;
use std::future::Future;
struct MetricsLayer {
registry: Arc<MetricsRegistry>,
}
impl<S> Layer<S> for MetricsLayer
where
S: Service<String>,
{
type Service = MetricsService<S>;
fn layer(&self, inner: S) -> Self::Service {
MetricsService {
inner,
registry: Arc::clone(&self.registry),
}
}
}
struct MetricsService<S> {
inner: S,
registry: Arc<MetricsRegistry>,
}
impl<S> Service<String> for MetricsService<S>
where
S: Service<String>,
{
type Response = S::Response;
type Error = S::Error;
type Future = MetricsFuture<S::Future>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: String) -> Self::Future {
self.registry.request_count.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
MetricsFuture {
inner: self.inner.call(req),
start: Instant::now(),
registry: Arc::clone(&self.registry),
}
}
}
struct MetricsFuture<F> {
inner: F,
start: Instant,
registry: Arc<MetricsRegistry>,
}
impl<F, T, E> Future for MetricsFuture<F>
where
F: Future<Output = Result<T, E>>,
{
type Output = Result<T, E>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = unsafe { self.get_unchecked_mut() };
let inner = unsafe { Pin::new_unchecked(&mut this.inner) };
match inner.poll(cx) {
Poll::Ready(result) => {
let duration = this.start.elapsed();
this.registry.latency.set(duration.as_millis() as u64);
Poll::Ready(result)
}
Poll::Pending => Poll::Pending,
}
}
}
struct MetricsRegistry {
request_count: std::sync::atomic::AtomicUsize,
latency: std::sync::atomic::AtomicU64,
}A complete metrics layer tracks request counts and latency.
Synthesis
Layer trait:
- Transforms one
Serviceinto another - Called once per service instantiation
- Holds configuration, not runtime state
- Returns a new
Servicethat wraps the inner service - Enables generic middleware composition
Service trait:
- Handles requests and returns responses
- Called per request (many times)
- Holds runtime state (counters, connections)
- Returns a
Futurefor async processing - The core abstraction for request handling
Layer pattern benefits:
- Declarative middleware composition
- Type-safe service stacks
- Generic over inner service types
- Reusable across different applications
- Clear separation of configuration and runtime
When to implement Layer:
- Creating reusable middleware
- Need configuration before service creation
- Sharing state across service instances
- Building middleware libraries
When Service alone is enough:
- Single service without middleware
- No composition requirements
- Direct instantiation is preferred
Key insight: Layer exists because middleware needs to be instantiated per-service, not per-request. Without Layer, you'd need to manually construct middleware stacks with the right configuration and inner servicesâLayer provides a uniform interface for that construction. The pattern is analogous to factory: Layer is the factory, Service is the product. This separation matters for frameworks like Axum where routes need middleware applied consistently across different handlers. Layer::layer() is called when the router is built; Service::call() is called for each incoming request. The middleware author implements Layer to create the service and Service to handle requests, while framework users compose layers declaratively without knowing the inner service types.
