Tower for Rust: Complete Guide with Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

What is Tower and why is it needed?

\n\n

Tower is a fundamental library of the Rust ecosystem, providing abstractions for building modular, composable, and asynchronous network services and middleware. It is not a ready-made HTTP framework (like Actix-web or Axum), but rather a "building block" for them. Axum, for example, is built on top of Tower.

\n\n

The main idea of Tower is the Service trait, which represents an asynchronous function that transforms a request (Request) into a response (Response). Middleware (or "Layer") in Tower is a way to wrap one service with another, adding additional behavior (logging, authentication, timeouts, load balancing, etc.) without changing the original service code.

\n\n

Why is this needed?

\n
    \n
  • Modularity: You can break down complex request processing logic into small, independent, reusable components (services and middleware).
  • \n
  • Composability: Middleware can be easily combined, "stacking" them on top of each other like layers.
  • \n
  • Asynchronicity: Tower is designed to work with asynchronous Rust (tokio, async-std) and supports futures.
  • \n
  • Testability: Each component can be tested in isolation.
  • \n
\n\n

Installing Tower

\n\n

Tower is a Rust library, so it is installed via the Cargo package manager. Add the dependency to your Cargo.toml file:

\n\n
[dependencies]\ntower = { version = "0.4", features = ["full"] } # "full" enables all features\ntokio = { version = "1", features = ["full"] } # For the async runtime\n
\n\n

Then run the following command in the terminal:

\n
cargo build
\n\n

The library will be downloaded and compiled together with your project.

\n\n

Quick Start: Your First Service and Middleware

\n\n

Let's create a simple service that takes a string and returns it in uppercase. Then we'll wrap it in middleware that logs each request.

\n\n
use std::convert::Infallible;\nuse std::task::{Context, Poll};\nuse tower::{Service, Layer, ServiceBuilder};\nuse tower::layer::util::Identity;\nuse std::future::{Ready, ready};\n\n// 1. Define our service\n#[derive(Clone)]\nstruct MyService;\n\nimpl<Request> Service<Request> for MyService {\n    type Response = String;\n    type Error = Infallible;\n    type Future = Ready<Result<Self::Response, Self::Error>>;\n\n    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        Poll::Ready(Ok(()))\n    }\n\n    fn call(&mut self, req: Request) -> Self::Future {\n        // Simply return a string\n        ready(Ok(format!("Hello from service! Request: {:?}", std::any::type_name::<Request>())))\n    }\n}\n\n// 2. Define middleware for logging\n#[derive(Clone)]\nstruct LogMiddleware;\n\nimpl<S> Layer<S> for LogMiddleware {\n    type Service = LogService<S>;\n\n    fn layer(&self, inner: S) -> Self::Service {\n        LogService { inner }\n    }\n}\n\n#[derive(Clone)]\nstruct LogService<S> {\n    inner: S,\n}\n\nimpl<S, Request> Service<Request> for LogService<S>\nwhere\n    S: Service<Request>,\n    S::Future: std::marker::Send,\n    Request: std::fmt::Debug,\n{\n    type Response = S::Response;\n    type Error = S::Error;\n    type Future = S::Future;\n\n    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        self.inner.poll_ready(cx)\n    }\n\n    fn call(&mut self, req: Request) -> Self::Future {\n        printl                    

Recommendations