---
title: "Axum State e FromRef: Estado Compartilhado em Rust"
url: "https://rustlang.com.br/blog/axum-state-fromref-estado-compartilhado/"
markdown_url: "https://rustlang.com.br/blog/axum-state-fromref-estado-compartilhado.MD"
description: "Aprenda a compartilhar estado no Axum com State, FromRef e Arc: monte routers, escolha locks e teste dependências sem confundir estado global e requisição."
date: "2026-10-12"
author: "Equipe Rust Brasil"
---

# Axum State e FromRef: Estado Compartilhado em Rust

Aprenda a compartilhar estado no Axum com State, FromRef e Arc: monte routers, escolha locks e teste dependências sem confundir estado global e requisição.


**No Axum, use `State` para injetar dependências da aplicação, `FromRef` para expor partes de `AppState` e `Arc` quando precisar compartilhar a mesma alocação entre handlers.** Não coloque automaticamente um `Mutex` ao redor de todo o estado: configuração imutável, pools de banco e contadores têm necessidades diferentes. Já `Extension` é apropriado para valores transportados nas extensões de uma requisição, frequentemente inseridos por middleware.

Este tutorial usa **Axum 0.8.6** e monta um servidor executável com configuração imutável, um contador compartilhado e rotas separadas. Não há banco nem autenticação. A intenção é entender a composição do estado antes de acrescentar dependências ao seu [backend Axum](/ecossistema/axum/).

## State, Extension ou uma variável global?

| Necessidade | Escolha inicial | Cuidado |
|---|---|---|
| Configuração carregada no startup | `State` | Evite ler arquivo ou ambiente a cada request |
| Pool SQLx ou cliente HTTP reutilizável | Campo de `AppState` | Muitos handles já são baratos de clonar |
| Dependência menor de um módulo | `State<T>` com `FromRef<AppState>` | A extração é síncrona; não faça I/O nela |
| Usuário autenticado por middleware | Extensões da requisição, lidas com `Extension` | A ordem de execução do middleware importa |
| Um contador independente | `Arc<AtomicU64>` | Atomicidade não resolve invariantes entre vários campos |
| Coleção mutável compartilhada | `Arc<Mutex<T>>` ou outra estrutura adequada | Considere duração do lock e concorrência |

Uma variável global pode funcionar em casos específicos, mas dificulta substituir dependências e isolar testes. Com `State`, a função de montagem declara o que a aplicação usa. Para persistência, veja a [API REST com PostgreSQL](/tutoriais/api-rest-axum/); aqui manteremos a infraestrutura fora do caminho.

## 1. Crie o projeto

Com uma toolchain stable atualizada e [Cargo instalado](/instalacao/cargo/):

```bash
cargo new axum-estado
cd axum-estado
```

Substitua `Cargo.toml` por:

```toml
[package]
name = "axum-estado"
version = "0.1.0"
edition = "2021"

[dependencies]
axum = { version = "=0.8.6", features = ["macros"] }
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread", "net"] }

[dev-dependencies]
tower = { version = "=0.5.2", features = ["util"] }
```

As versões exatas de Axum e Tower delimitam as APIs do exemplo, não indicam as versões mais recentes. Guarde o `Cargo.lock` para registrar também as versões transitivas. A feature `macros` de Axum é habilitada explicitamente para disponibilizar o derive de `FromRef`; ela não faz parte das features padrão.

## 2. Monte o estado e os routers

Substitua `src/main.rs` pelo código completo:

```rust
use axum::{
    extract::{FromRef, State},
    routing::get,
    Json, Router,
};
use serde::Serialize;
use std::sync::{
    atomic::{AtomicU64, Ordering},
    Arc,
};

#[derive(Clone)]
struct Configuracao {
    nome: Arc<str>,
}

#[derive(Clone, FromRef)]
struct AppState {
    config: Configuracao,
    visitas: Arc<AtomicU64>,
}

impl AppState {
    fn novo(nome: &str) -> Self {
        Self {
            config: Configuracao {
                nome: Arc::from(nome),
            },
            visitas: Arc::new(AtomicU64::new(0)),
        }
    }
}

#[derive(Serialize)]
struct Info {
    nome: String,
}

#[derive(Serialize)]
struct Visitas {
    total: u64,
}

async fn info(State(config): State<Configuracao>) -> Json<Info> {
    Json(Info {
        nome: config.nome.to_string(),
    })
}

async fn registrar_visita(
    State(visitas): State<Arc<AtomicU64>>,
) -> Json<Visitas> {
    let total = visitas.fetch_add(1, Ordering::Relaxed) + 1;
    Json(Visitas { total })
}

fn rotas_info() -> Router<AppState> {
    Router::new().route("/info", get(info))
}

fn app(state: AppState) -> Router {
    Router::new()
        .merge(rotas_info())
        .route("/visitas", get(registrar_visita))
        .with_state(state)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
    axum::serve(listener, app(AppState::novo("API de estudo"))).await?;
    Ok(())
}
```

Execute `cargo run`. Em outro terminal:

```bash
curl http://127.0.0.1:3000/info
curl http://127.0.0.1:3000/visitas
curl http://127.0.0.1:3000/visitas
```

`/info` retorna `{"nome":"API de estudo"}`. Em um processo recém-iniciado, as duas chamadas sequenciais a `/visitas` retornam `{"total":1}` e `{"total":2}`. Reiniciar o processo zera o contador: **estado compartilhado em memória não é persistência**.

O endpoint GET que incrementa o contador é apenas uma demonstração. Não o trate como uma API de negócio cujo GET deveria ser seguro e livre de efeitos solicitados pelo cliente. Prefira métricas em middleware para contar tráfego, ou um método como POST quando a operação contratada é registrar um evento.

## 3. Entenda o que FromRef faz

O derive de `FromRef` gera implementações para obter cada tipo de campo a partir de `&AppState`. No exemplo, o handler `info` precisa somente de `Configuracao`; ele não conhece o contador. O handler de visitas pede apenas `Arc<AtomicU64>`.

A implementação equivalente para a configuração seria:

```rust
impl FromRef<AppState> for Configuracao {
    fn from_ref(state: &AppState) -> Self {
        state.config.clone()
    }
}
```

Esse trecho é uma **alternativa** à implementação gerada para esse campo, não um bloco para adicionar junto ao derive existente. Implementar o mesmo trait para o mesmo par de tipos duas vezes gera conflito.

`FromRef` não é um container de injeção de dependências nem um lookup por nome. A resolução acontece por tipos. Se dois campos têm o mesmo tipo, o derive pode tentar gerar implementações conflitantes. Use newtypes para distinguir, por exemplo, `BancoLeitura` e `BancoEscrita`, ou implemente manualmente a seleção desejada. Não abra conexões nem faça consultas dentro de `from_ref`: prepare recursos no startup.

## 4. Por que Router<AppState> ainda precisa de estado?

O parâmetro de `Router<S>` representa o estado que **falta fornecer**, não simplesmente um objeto que já está guardado dentro dele. `rotas_info()` devolve `Router<AppState>` porque seus handlers dependem de valores extraíveis de `AppState`.

A função `app()` une os routers e chama `with_state(state)`. Seu retorno `Router`, isto é, `Router<()>`, indica que nenhuma dependência de estado ficou pendente para servir a aplicação.

Essa distinção ajuda a interpretar erros em `merge`, `nest` e `axum::serve`:

- Se os subrouters serão montados juntos, declare um tipo de estado compatível entre eles.
- Forneça o estado na fronteira de composição escolhida, como fazemos em `app()`.
- Não conclua que todo erro envolvendo `Router<AppState>` exige adicionar outro `Arc`; muitas vezes o problema é um estado ainda não fornecido ou uma anotação incompatível.

`with_state` também permite escolher outro tipo de estado pendente em composições mais avançadas. O retorno explícito de `app()` mantém este exemplo no caso simples e evita ambiguidades.

## 5. Clone, Arc e locks não significam a mesma coisa

O derive de `Clone` em `AppState` clona seus campos. Aqui, clonar `Arc` mantém a mesma alocação: todas as cópias do estado enxergam o mesmo contador. Isso não faz uma cópia independente de cada visita.

`Arc<T>` permite propriedade compartilhada, mas **não** oferece mutação arbitrária de `T`. A mutação do exemplo é possível porque `AtomicU64` disponibiliza operações atômicas. `Ordering::Relaxed` é suficiente para esse contador independente: não usamos seu valor para publicar ou sincronizar outros dados. Não copie essa escolha para protocolos de concorrência com invariantes adicionais. Veja [atomics em Rust](/stdlib/atomics/).

Para dados mais complexos, escolha a sincronização pelo acesso real:

- `std::sync::Mutex` pode servir para uma seção crítica curta, sem `.await`, com baixa contenção. Seu lock bloqueia a thread enquanto espera.
- `tokio::sync::Mutex` permite aguardar o lock de modo assíncrono. É útil quando realmente precisa manter exclusividade durante uma operação assíncrona, mas não elimina gargalos.
- Um pool como `sqlx::PgPool` já gerencia concorrência e compartilhamento. Envolver o pool inteiro em um mutex normalmente só serializa acesso desnecessariamente.

Evite segurar um guard de `std::sync::Mutex` através de `.await`. Além do risco de bloquear o executor, isso pode tornar a future incompatível com os requisitos do handler. Copie ou transforme o necessário em um escopo curto, solte o lock e só depois aguarde I/O. Consulte o [guia de Mutex](/stdlib/mutex/) e o diagnóstico [future não é Send](/erros/future-not-send/).

## 6. Teste compartilhamento e isolamento

Acrescente ao final de `src/main.rs`:

```rust
#[cfg(test)]
mod tests {
    use super::*;
    use axum::{
        body::{to_bytes, Body},
        http::{Request, StatusCode},
    };
    use tower::ServiceExt;

    async fn visitar(router: Router) -> String {
        let request = Request::builder()
            .uri("/visitas")
            .body(Body::empty())
            .unwrap();
        let response = router.oneshot(request).await.unwrap();
        assert_eq!(response.status(), StatusCode::OK);
        let bytes = to_bytes(response.into_body(), 1024).await.unwrap();
        String::from_utf8(bytes.to_vec()).unwrap()
    }

    #[tokio::test]
    async fn clones_compartilham_contador() {
        let router = app(AppState::novo("teste"));
        assert_eq!(visitar(router.clone()).await, r#"{"total":1}"#);
        assert_eq!(visitar(router).await, r#"{"total":2}"#);
    }

    #[tokio::test]
    async fn estados_novos_sao_independentes() {
        let primeiro = app(AppState::novo("primeiro"));
        let segundo = app(AppState::novo("segundo"));
        assert_eq!(visitar(primeiro).await, r#"{"total":1}"#);
        assert_eq!(visitar(segundo).await, r#"{"total":1}"#);
    }
}
```

Rode `cargo test`: os dois testes devem passar sem abrir uma porta. O primeiro comprova que clones do router compartilham o recurso. O segundo comprova que construir um **novo** `AppState` produz outra fixture. Clonar estado não isola testes; recriar ou substituir suas dependências pode isolar.

Comparamos aqui um JSON minúsculo de um único campo como texto. Para respostas maiores, prefira comparar valores desserializados, como no tutorial de [testes Axum sem servidor HTTP](/blog/testar-api-axum-sem-servidor-http/).

## Perguntas frequentes

### State substitui Extension em todos os casos?

Não. `State` expressa dependências da aplicação no tipo do router. `Extension` lê um valor das extensões da requisição e é útil quando middleware insere informações daquele request. Usar `Extension<T>` obrigatório sem instalar o valor provoca uma rejection em runtime; com `State`, a compatibilidade do estado é verificada na composição dos tipos.

### Preciso de Arc<AppState>?

Não necessariamente. Neste projeto, `AppState` já contém campos compartilhados e baratos de clonar. Envolver todo o objeto em `Arc` é outra opção válida, mas muda o tipo de estado e as implementações de `FromRef` necessárias. Escolha uma estratégia consistente em vez de empilhar wrappers sem necessidade.

### FromRef deixa o handler independente de tudo?

Ele reduz o acoplamento ao estado principal, mas o handler continua dependente do tipo solicitado. Para substituir um cliente real por uma implementação de teste, você ainda precisa projetar essa dependência — por exemplo, com traits ou uma camada de serviço. `FromRef` apenas faz a extração.

### Esse contador funciona entre várias instâncias?

Não. Cada processo tem seu próprio `AppState`. Para um valor global entre réplicas, escolha armazenamento externo e defina consistência, durabilidade e política de falhas. Para contagem de tráfego, use uma estratégia de [métricas Prometheus](/blog/prometheus-metrics-rust-axum-producao-2026/) adequada à agregação de instâncias.

## Checklist e referências

Antes de adicionar uma dependência ao estado, confirme:

- Ela é criada no startup, não em cada handler?
- Clonar o handle compartilha o recurso ou copia dados caros?
- O handler precisa do estado inteiro ou de uma parte via `FromRef`?
- Há lock atravessando `.await` ou um gargalo por contenção?
- Os testes criam fixtures independentes quando necessário?
- A aplicação depende de memória local ou exige persistência entre processos?

Referências das APIs usadas:

- [State em Axum 0.8.6](https://docs.rs/axum/0.8.6/axum/extract/struct.State.html)
- [FromRef em Axum 0.8.6](https://docs.rs/axum/0.8.6/axum/extract/trait.FromRef.html)
- [Router::with_state](https://docs.rs/axum/0.8.6/axum/struct.Router.html#method.with_state)
- [Extension e suas rejections](https://docs.rs/axum/0.8.6/axum/struct.Extension.html)

Como próximo exercício, substitua o contador por um serviço de aplicação e teste duas requisições que usam a mesma dependência. Para organizar os fundamentos antes de ampliar o backend, siga a trilha do [curso de Rust](/curso/).
