diff --git a/axum/src/lib.rs b/axum/src/lib.rs index ba32651b..a550d9e4 100644 --- a/axum/src/lib.rs +++ b/axum/src/lib.rs @@ -186,7 +186,76 @@ //! State is cloned for every request. Wrapping your state in `Arc` makes those //! clones cheap. If all fields are already cheap to clone (for example, each field //! is itself an `Arc` or a copy type), you can `#[derive(Clone)]` directly on the -//! struct instead. +//! struct instead. Many client types, such as [`reqwest::Client`], AWS SDK service +//! clients, and [`mongodb::Client`], already use shared ownership internally and +//! are cheap to clone. Such clients generally do not need another `Arc` solely to +//! make cloning cheap; doing so adds another level of indirection. +//! +//! [`reqwest::Client`]: https://docs.rs/reqwest/latest/reqwest/struct.Client.html +//! [`mongodb::Client`]: https://docs.rs/mongodb/latest/mongodb/struct.Client.html +//! +//! ### Using `&'static` state +//! +//! For state built once and intended to live until the process exits, [`Box::leak`] +//! or a static [`LazyLock`] can provide a `&'static AppState` to the router. Use +//! `Box::leak` for state initialized in `main`, including when state initialization +//! requires asynchronous work; use a static `LazyLock` for global state with a +//! synchronous lazy initializer. This is useful for state built from runtime +//! configuration, database pools, or service clients: +//! +//! ```rust +//! use axum::{ +//! extract::State, +//! routing::get, +//! Router, +//! }; +//! +//! struct AppState { +//! // A database pool, service clients, configuration, etc. +//! } +//! +//! impl AppState { +//! async fn work(&self) {} +//! } +//! +//! fn main() { +//! let app_state = AppState { +//! // Initialize fields at startup. +//! }; +//! let app_state: &'static AppState = Box::leak(Box::new(app_state)); +//! +//! let app = Router::new() +//! .route("/", get(handler)) +//! .with_state(app_state); +//! # let _: Router = app; +//! } +//! +//! async fn handler(State(state): State<&'static AppState>) { +//! let _task = tokio::spawn(async move { +//! state.work().await; +//! }); +//! +//! // `state` is still available in the handler because it was copied. +//! state.work().await; +//! } +//! ``` +//! +//! Like every shared reference, a `&'static T` is `Copy`, so axum can clone it for +//! each request without an atomic reference-count operation. This can be cheaper than +//! cloning an `Arc`, but it is rarely a reason on its own to choose process-lifetime +//! state since the request's actual work normally matters more. The more practical +//! advantage of combining `Copy` with the `'static` lifetime is shown above: an +//! `async move` block copies the reference into the spawned Tokio task while leaving +//! the handler's copy available. With `Arc`, call [`Arc::clone`] before spawning when +//! the handler also needs the state. +//! +//! `Box::leak` deliberately leaks its allocation, and state held by a static +//! `LazyLock` is likewise never dropped. Use either only for process-lifetime state. +//! Use `Arc` when the state needs a managed lifetime. +//! +//! [`Arc::clone`]: https://doc.rust-lang.org/std/sync/struct.Arc.html#method.clone +//! [`Box::leak`]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.leak +//! [`LazyLock`]: https://doc.rust-lang.org/std/sync/struct.LazyLock.html //! //! ### Substates with `FromRef` //!