Módulos e o sistema de visibilidade
1. Introdução ao Sistema de Módulos
Em Rust, módulos são a principal ferramenta para organizar código, encapsular implementações e promover reutilização. Eles permitem agrupar funcionalidades relacionadas, controlar o que é exposto como API pública e criar hierarquias lógicas dentro de um crate.
A árvore de módulos reflete a estrutura do sistema de arquivos. Existem duas convenções principais:
- Estilo 2015: src/modulo/mod.rs
- Estilo 2018+: src/modulo.rs (recomendado)
Para declarar um módulo, usamos a palavra-chave mod:
// src/main.rs
mod utils; // procura por utils.rs ou utils/mod.rs
2. Definindo e Aninhando Módulos
Módulos podem ser definidos inline ou em arquivos separados:
// Módulo inline
mod calculos {
fn somar(a: i32, b: i32) -> i32 {
a + b
}
}
// Módulo em arquivo separado (src/matematica.rs)
mod matematica;
// Submódulo (src/matematica/geometria.rs)
mod matematica {
pub mod geometria;
}
Por padrão, tudo dentro de um módulo é privado. Isso significa que itens definidos em um módulo não são acessíveis externamente a menos que explicitamente tornados públicos.
3. Controle de Visibilidade com pub
A palavra-chave pub torna itens públicos:
pub mod utilidades {
pub fn saudacao() -> String {
"Olá!".to_string()
}
fn interna() {
// Função privada
}
pub struct Config {
pub nome: String, // Campo público
versao: u32, // Campo privado
}
pub enum Status {
Ativo, // Variantes públicas (se o enum é pub)
Inativo,
}
}
Para structs, cada campo precisa ser explicitamente marcado como pub para ser acessível externamente. Já para enums, se o enum é público, todas as variantes são públicas automaticamente.
4. Visibilidade Restrita com pub(crate) e pub(super)
Rust oferece níveis de visibilidade mais granulares:
pub(crate) fn funcao_interna_ao_crate() {
// Visível apenas dentro do crate atual
}
pub(super) fn funcao_do_modulo_pai() {
// Visível apenas no módulo pai
}
mod nivel1 {
pub(super) fn ajuda() {} // Visível no módulo pai
pub mod nivel2 {
pub fn publica() {}
pub(crate) fn interna() {} // Visível em todo o crate
}
}
Caminhos relativos e absolutos ajudam a navegar na hierarquia:
use crate::modulo::Item; // Caminho absoluto
use super::Item; // Caminho relativo ao pai
use self::Item; // Caminho relativo ao módulo atual
5. A Palavra-chave use e Reexportação
use traz itens para o escopo atual:
// src/main.rs
use crate::utilidades::saudacao;
use std::collections::HashMap;
// Reexportação com pub use
pub mod internals {
pub fn helper() -> u32 { 42 }
}
pub use internals::helper; // Reexporta helper como parte da API pública
// Apelidos com as
use std::collections::HashMap as Map;
use crate::utilidades::saudacao as ola;
Reexportação é poderosa para criar APIs limpas, escondendo a estrutura interna do módulo.
6. Módulos e o Sistema de Arquivos
Estrutura típica de um projeto:
meu_projeto/
├── src/
│ ├── main.rs
│ ├── lib.rs
│ ├── modulo.rs # Módulo raiz
│ ├── modulo/
│ │ ├── mod.rs # (estilo 2015)
│ │ └── submodulo.rs
│ └── utils/
│ ├── mod.rs
│ └── string_utils.rs
No estilo 2018+, preferimos src/modulo.rs em vez de src/modulo/mod.rs. Para projetos grandes, organize por funcionalidade:
// src/lib.rs
pub mod auth;
pub mod database;
pub mod api;
// src/auth.rs
pub mod login;
pub mod tokens;
7. Visibilidade de Tipos e Traits
Encapsulamento com structs públicas e campos privados:
pub struct BancoDeDados {
conexao: String, // Privado
pub nome: String, // Público
}
impl BancoDeDados {
pub fn new(nome: &str) -> Self {
BancoDeDados {
conexao: format!("localhost/{}", nome),
nome: nome.to_string(),
}
}
pub fn conectar(&self) {
// Método público
}
}
Traits podem ser públicos ou privados:
pub trait Serializavel {
fn para_json(&self) -> String;
}
trait Interno {
fn processar(&self);
}
Módulos pub mod com conteúdo seletivamente público:
pub mod api {
pub fn endpoint() {} // Público
fn interno() {} // Privado
pub struct Resposta { // Público, mas campos privados
dados: Vec<u8>,
}
}
8. Exemplo Prático: Projeto com Módulos e Visibilidade
Vamos criar uma biblioteca de processamento de texto:
// src/lib.rs
pub mod processamento;
pub mod formatacao;
pub use processamento::analisar;
pub use formatacao::formatar;
// src/processamento.rs
mod tokenizador;
mod analisador;
pub fn analisar(texto: &str) -> Result<String, String> {
let tokens = tokenizador::tokenizar(texto)?;
Ok(analisador::analisar(&tokens))
}
// src/processamento/tokenizador.rs
pub(crate) fn tokenizar(texto: &str) -> Result<Vec<String>, String> {
Ok(texto.split_whitespace().map(String::from).collect())
}
// src/formatacao.rs
pub fn formatar(texto: &str) -> String {
texto.trim().to_string()
}
Testando privacidade:
// Isso compila:
use minha_biblioteca::analisar;
let resultado = analisar("exemplo").unwrap();
// Isso NÃO compila (tokenizador é privado):
// use minha_biblioteca::processamento::tokenizador;
// Isso compila (pub use reexportou):
use minha_biblioteca::formatar;
Erros comuns:
- Tentar acessar item privado de outro módulo
- Esquecer pub em campos de struct
- Não usar pub use para reexportar itens aninhados
Conclusão
O sistema de módulos e visibilidade em Rust oferece controle granular sobre a organização e exposição do código. Com pub, pub(crate), pub(super) e reexportação via pub use, é possível criar APIs limpas e seguras, escondendo detalhes de implementação enquanto expõe apenas o necessário. A relação com o sistema de arquivos torna a navegação intuitiva, e as regras de privacidade padrão incentivam o encapsulamento desde o início.
Referências
- The Rust Reference: Modules — Documentação oficial sobre declaração e uso de módulos em Rust
- The Rust Book: Defining Modules to Control Scope and Privacy — Capítulo completo sobre módulos, caminhos e visibilidade
- Rust by Example: Modules — Exemplos práticos de módulos, visibilidade e hierarquia
- Rust Module System Explained — Artigo técnico detalhando o sistema de módulos com exemplos
- The Rust Edition Guide: Path Clarity — Guia sobre mudanças no sistema de módulos entre edições 2015 e 2018