Capítulo 11: Modularização, Crates, Visibilidade (pub) e Workspaces

Especialização em Sistemas com Rust • Rust 2021 Edition • Ownership, Concorrência Segura e Sistemas de Alta Performance


🗺️ Mapa Conceitual do Sistema de Módulos e Árvore de Visibilidade

flowchart TD
    CrateRoot["Crate Root (src/lib.rs ou src/main.rs)"] --> ModuloRede["mod rede (pub mod)"]
    CrateRoot --> ModuloSeguranca["mod seguranca (privado por padrão)"]

    subgraph REDE_INTERNO["Módulo de Rede (src/rede.rs)"]
        TCP["pub struct ConexaoTcp"]
        UDP["struct BufferUdp (privado ao módulo)"]
    end

    subgraph SEGURANCA_INTERNO["Módulo de Segurança (src/seguranca.rs)"]
        Cripto["pub(crate) fn criptografar(): Visível em toda a crate"]
        Hash["fn gerar_salt(): Privado estrito"]
    end

    ModuloRede --- REDE_INTERNO
    ModuloSeguranca --- SEGURANCA_INTERNO

    TCP -->|"pub use rede::ConexaoTcp (Reexportação Fachada)"| ApiPublica["API Pública Limpa da Crate"]

    style CrateRoot fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style ModuloRede fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style ModuloSeguranca fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
    style ApiPublica fill:#e8f5e9,stroke:#4caf50,stroke-width:2px

🏛️ 1. A Filosofia de Modularização no Rust

A medida que sistemas corporativos crescem em complexidade, manter todo o código em um único arquivo ou estrutura plana torna a manutenção inviável. O sistema de módulos do Rust foi desenhado para atender a três objetivos simultâneos:

  1. Encapsulamento Estrito: No Rust, tudo é privado por padrão. Nenhuma função, struct ou campo vaza para fora do seu escopo a menos que o desenvolvedor utilize explicitamente o modificador pub.
  2. Navegação Determinística: A árvore de módulos reflete diretamente a estrutura de arquivos no disco, com resolução de caminhos inequívoca (crate::, super::, self::).
  3. Escala Monorepo: O suporte nativo a Cargo Workspaces permite compilar dezenas de crates interdependentes em paralelo com compartilhamento atômico de dependências.

⚙️ 2. A Estrutura de Arquivos Moderna (Edição 2021)

A partir da edição 2018 e consolidada na 2021, o Rust eliminou a obrigatoriedade de arquivos mod.rs antigos. A convenção moderna adota:

src/
├── main.rs            <- Crate Root do binário
├── rede.rs            <- Declaração do módulo 'rede'
└── rede/              <- Diretório para submódulos de 'rede'
    ├── tcp.rs         <- Submódulo 'rede::tcp'
    └── dns.rs         <- Submódulo 'rede::dns'

No src/main.rs:

// Declara o módulo 'rede' que o compilador busca em 'src/rede.rs'
pub mod rede;

fn main() {
    rede::tcp::conectar("127.0.0.1:8080");
}

🔒 3. Os Níveis de Visibilidade e Controle de Acesso

O Rust oferece controle cirúrgico de visibilidade para construir interfaces bem arquitetadas:

Modificador de Visibilidade Escopo de Acesso Permitido
(Nenhum - Padrão) Privado: Acessível apenas dentro do módulo atual e de seus filhos
pub Público Total: Acessível por qualquer módulo e por crates externas
pub(crate) Escopo de Crate: Visível em qualquer arquivo dentro da crate atual, mas invisível externamente
pub(super) Escopo Pai: Visível exclusivamente dentro do módulo pai imediato
pub(in caminho) Caminho Restrito: Visível apenas dentro do submódulo especificado

Exemplo de Encapsulamento com Structs:

Em structs públicas, seus campos permanecem privados a menos que sejam explicitamente marcados com pub:

pub struct CredencialAuth {
    pub usuario: String,  // Campo público
    chave_secreta: String // Campo PRIVADO: impede instanciação direta e mutação indevida!
}

impl CredencialAuth {
    // Construtor obrigatório para controlar a criação de 'chave_secreta'
    pub fn new(usuario: &str, segredo: &str) -> Self {
        Self {
            usuario: String::from(usuario),
            chave_secreta: String::from(segredo),
        }
    }
}

🚪 4. Reexportação de Fachada com pub use

Muitas vezes, a organização interna de arquivos de uma biblioteca possui diretórios profundos (ex: crate::subsistema::protocolo::drivers::conexao::DriverTcp), mas o usuário final da biblioteca deseja apenas minha_crate::DriverTcp.

Utiliza-se pub use para criar uma API externa limpa (Façade Pattern):

// Em src/lib.rs:
pub mod subsistema;

// Reexportação que simplifica a vida do consumidor:
pub use subsistema::protocolo::drivers::conexao::DriverTcp;

🏢 5. Cargo Workspaces para Arquiteturas Corporativas

Quando um projeto é composto por múltiplos serviços e bibliotecas compartilhadas, utiliza-se um Cargo Workspace. Um workspace compartilha o mesmo diretório target/ de compilação e um único arquivo Cargo.lock:

# Cargo.toml (Na raiz do repositório)
[workspace]
members = [
    "crates/kernel-core",
    "crates/network-engine",
    "services/api-gateway",
    "apps/cli-admin",
]
resolver = "2"

Vantagens do Workspace:

  1. Compilação Incremental Compartilhada: Se a crate kernel-core for compilada por um serviço, os demais a reutilizam sem recompilar.
  2. Versões Unificadas: Garante que todas as crates usem rigorosamente as mesmas versões de dependências externas (como serde ou tokio).

⚠️ 6. Armadilhas Comuns e Mensagens do Compilador

  1. Esquecer pub em Campos de Structs Públicas (E0616):
    • Marcar pub struct MinhaStruct não torna seus campos públicos! Se tentar acessar instancia.campo de outro módulo, o compilador acusará: field is private.
    • Correção: Adicione pub antes de cada campo que deve ser exposto.
  2. Confundir use com Inclusão de Código:
    • O comando mod arquivo; é quem diz ao compilador para ler e compilar o arquivo.
    • O comando use caminho::Item; é apenas um atalho sintático para não precisar digitar o caminho completo. Nunca use use sem ter declarado o módulo correspondente com mod.

🛡️ 7. Boas Práticas de Arquitetura em Rust

  • Projete APIs com Visibilidade Mínima: Comece tudo como privado. Só promova para pub(crate) se múltiplos módulos precisarem daquele recurso, e só promova para pub se for parte formal do contrato da biblioteca.
  • Separe lib.rs de main.rs: Crie o núcleo da sua aplicação dentro de uma biblioteca (lib.rs) e use o main.rs apenas como um cliente de inicialização magro (thin client). Isso viabiliza testes de integração completos e reutilização por outras ferramentas.

📋 8. Cheat-Sheet: Resumo Executivo

// Declaração de módulo
pub mod servico;

// Importação e atalho
use crate::servico::Processador;
pub use crate::servico::Processador; // Reexporta

// Visibilidade de crate
pub(crate) fn interno() { /* ... */ }