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 TimeoutLayer

Layers 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;
// ... implementations

Layer 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;
// ... implementations

Tower 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 Service into another
  • Called once per service instantiation
  • Holds configuration, not runtime state
  • Returns a new Service that 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 Future for 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.