Pacotes, crates e módulos com Cargo
1. Introdução ao Sistema de Organização de Código em Rust
Rust oferece um sistema sofisticado para organizar código em diferentes níveis de abstração: pacotes, crates e módulos. Essa hierarquia permite que desenvolvedores estruturem projetos de qualquer tamanho de forma limpa e gerenciável.
- Pacote: É a unidade mais externa, contendo um ou mais crates. Cada pacote possui um arquivo
Cargo.tomlque define metadados e dependências. - Crate: Representa uma unidade de compilação. Pode ser binário (executável) ou de biblioteca (reutilizável).
- Módulo: Organiza o código dentro de um crate, controlando visibilidade e escopo.
O Cargo, gerenciador de pacotes do Rust, automatiza a criação e gerenciamento dessa estrutura, permitindo foco no desenvolvimento.
2. Estrutura de um Pacote com Cargo
Para criar um novo pacote, utilize o comando:
cargo new meu_projeto
Isso gera a seguinte estrutura:
meu_projeto/
├── Cargo.toml
└── src/
└── main.rs
O arquivo Cargo.toml contém metadados e dependências:
[package]
name = "meu_projeto"
version = "0.1.0"
edition = "2021"
[dependencies]
Por padrão, cargo new cria um crate binário com src/main.rs. Para criar uma biblioteca, use cargo new --lib:
cargo new minha_biblioteca --lib
Isso gera src/lib.rs em vez de main.rs.
3. Trabalhando com Crates Binários e de Biblioteca
Um pacote pode conter múltiplos crates binários. Para adicionar um segundo binário, crie o diretório src/bin/:
// src/bin/ferramenta.rs
fn main() {
println!("Ferramenta executada!");
}
Para executar: cargo run --bin ferramenta.
Crates de biblioteca expõem funcionalidades com pub:
// src/lib.rs
pub fn saudacao() -> String {
String::from("Olá do crate de biblioteca!")
}
É possível combinar ambos no mesmo pacote:
// src/main.rs
use meu_projeto::saudacao;
fn main() {
println!("{}", saudacao());
}
4. Definindo e Organizando Módulos
Módulos podem ser declarados diretamente no arquivo raiz:
// src/lib.rs
mod utils;
pub fn processar() {
utils::ajudante();
}
// src/utils.rs
pub fn ajudante() {
println!("Ajudante executado");
}
Para submódulos, use pastas:
// src/utils/mod.rs
pub mod formatacao;
pub fn ajudante() {
formatacao::formatar();
}
// src/utils/formatacao.rs
pub fn formatar() {
println!("Formatando...");
}
A abordagem moderna (Rust 2018+) permite arquivos sem mod.rs:
src/
├── utils/
│ └── formatacao.rs
└── utils.rs
5. Sistema de Visibilidade e Controle de Acesso
O modificador pub controla a visibilidade:
// src/lib.rs
mod interno {
pub fn publica() {}
fn privada() {}
pub(crate) fn visivel_no_crate() {}
pub(super) fn visivel_no_modulo_pai() {}
}
pub: visível para todospub(crate): visível apenas dentro do crate atualpub(super): visível apenas no módulo pai- Sem modificador: privado ao módulo atual
Exemplo prático:
mod autenticacao {
pub struct Usuario {
pub nome: String,
senha: String, // privado
}
impl Usuario {
pub fn novo(nome: String, senha: String) -> Self {
Usuario { nome, senha }
}
pub fn verificar_senha(&self, tentativa: &str) -> bool {
self.senha == tentativa
}
}
}
6. Navegando e Importando com use e as
O comando use simplifica o acesso a itens:
use std::collections::HashMap;
let mut mapa = HashMap::new();
Renomeando com as:
use std::collections::HashMap as Mapa;
let mut mapa = Mapa::new();
Re-exportação com pub use:
// src/lib.rs
mod formatacao {
pub fn texto_negrito(s: &str) -> String {
format!("**{}**", s)
}
}
pub use formatacao::texto_negrito;
Isso permite que consumidores usem texto_negrito diretamente, sem conhecer o módulo interno.
7. Gerenciando Dependências Externas com Cargo
Para adicionar uma dependência externa, edite o Cargo.toml:
[dependencies]
serde = { version = "1.0", features = ["derive"] }
reqwest = "0.11"
tokio = { version = "1", features = ["full"] }
Dependências de desenvolvimento (apenas para testes):
[dev-dependencies]
criterion = "0.5"
O Cargo usa versionamento semântico (SemVer). Por exemplo, "0.11" permite qualquer versão 0.11.x mas não 0.12.0.
Exemplo prático de uso:
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct Dados {
nome: String,
valor: i32,
}
8. Boas Práticas e Padrões de Projeto
Para projetos grandes, organize módulos por funcionalidade:
src/
├── main.rs
├── api/
│ ├── mod.rs
│ ├── handlers.rs
│ └── modelos.rs
├── db/
│ ├── mod.rs
│ ├── conexao.rs
│ └── queries.rs
└── config/
├── mod.rs
└── ambiente.rs
Separe responsabilidades em múltiplos crates:
meu_projeto/
├── Cargo.toml (workspace)
├── core/
│ ├── Cargo.toml
│ └── src/lib.rs
├── web/
│ ├── Cargo.toml
│ └── src/lib.rs
└── cli/
├── Cargo.toml
└── src/main.rs
Para evitar dependências circulares, use inversão de dependência: crie um crate de interface (trait) que ambos os lados implementam.
Conclusão
Dominar pacotes, crates e módulos é essencial para escrever código Rust limpo, reutilizável e de fácil manutenção. O sistema de módulos com visibilidade controlada, combinado com o gerenciamento eficiente de dependências do Cargo, oferece uma base sólida para projetos de qualquer escala.
Referências
- The Rust Programming Language - Chapter 7: Managing Growing Projects with Packages, Crates, and Modules — Capítulo oficial do livro de Rust sobre organização de código
- Rust by Example - Modules — Exemplos práticos de declaração e uso de módulos
- Cargo Documentation - Package Layout — Guia oficial sobre estrutura de projetos com Cargo
- Rust Module System Explained — Artigo técnico detalhado sobre o sistema de módulos em Rust
- The Cargo Book - Dependencies — Documentação oficial sobre especificação de dependências no Cargo.toml
- Rust Visibility and Privacy — Referência oficial sobre modificadores de visibilidade em Rust