Loading page…
Rust walkthroughs
Loading page…
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Layer trait:
Service into anotherService that wraps the inner serviceService trait:
Future for async processingLayer pattern benefits:
When to implement Layer:
When Service alone is enough:
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.