Cargo Rust: Gerenciador de Pacotes Guia Completo | Rust Brasil

Guia completo do Cargo, build system do Rust. Cargo.toml, dependências, workspaces, profiles, features e plugins essenciais.

O Cargo é muito mais do que um simples gerenciador de pacotes — ele é o coração do ecossistema Rust. Desde a criação de projetos até a publicação de crates no crates.io, o Cargo gerencia dependências, compila código, executa testes, gera documentação e orquestra todo o fluxo de trabalho de desenvolvimento Rust.

Todo projeto Rust utiliza o Cargo. Ele foi projetado para ser determinístico, reproduzível e eficiente, garantindo que builds funcionem de maneira consistente em qualquer máquina. Se você já usou npm, pip ou Maven, vai se sentir em casa — mas com garantias muito mais fortes.

Neste guia, vamos explorar desde os fundamentos até recursos avançados como workspaces, build profiles customizados, features flags e plugins que turbinarão sua produtividade.

Instalação

O Cargo vem instalado automaticamente junto com o Rust quando você usa o rustup:

# Instalar Rust e Cargo via rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Verificar a instalação
cargo --version
# cargo 1.77.0 (3fe68eabf 2024-02-29)

Se você já tem o Rust instalado, o Cargo já está disponível. Para atualizar:

# Atualizar Rust e Cargo para a versão mais recente
rustup update stable

Uso Básico

Criando um Novo Projeto

# Criar um novo projeto binário (executável)
cargo new meu-projeto
cd meu-projeto

# Criar um novo projeto de biblioteca
cargo new minha-lib --lib

# Inicializar Cargo em um diretório existente
cargo init
cargo init --lib  # como biblioteca

Estrutura de um Projeto Cargo

Ao criar um projeto, o Cargo gera a seguinte estrutura:

meu-projeto/
├── Cargo.toml       # Manifesto do projeto
├── Cargo.lock       # Lockfile com versões exatas (committar para binários)
├── src/
│   ├── main.rs      # Ponto de entrada para binários
│   └── lib.rs       # Ponto de entrada para bibliotecas
├── tests/           # Testes de integração
├── benches/         # Benchmarks
├── examples/        # Exemplos de uso
└── target/          # Artefatos de build (ignorar no git)

Estrutura do Cargo.toml

O Cargo.toml é o manifesto do projeto, escrito em formato TOML:

[package]
name = "meu-projeto"
version = "0.1.0"
edition = "2021"
authors = ["Seu Nome <seu@email.com>"]
description = "Uma descrição do meu projeto"
license = "MIT OR Apache-2.0"
repository = "https://github.com/usuario/meu-projeto"
readme = "README.md"
keywords = ["rust", "exemplo"]
categories = ["development-tools"]
rust-version = "1.70"  # Versão mínima do Rust

[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
anyhow = "1.0"

[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
tempfile = "3"

[build-dependencies]
cc = "1.0"

[profile.release]
opt-level = 3
lto = true

Comandos Essenciais

# Compilar o projeto (modo debug)
cargo build

# Compilar em modo release (otimizado)
cargo build --release

# Compilar e executar
cargo run

# Executar com argumentos
cargo run -- --flag argumento

# Verificar se compila sem gerar binário (mais rápido)
cargo check

# Executar todos os testes
cargo test

# Executar testes com saída do println!
cargo test -- --nocapture

# Executar um teste específico
cargo test nome_do_teste

# Gerar documentação
cargo doc --open

# Limpar artefatos de build
cargo clean

# Atualizar dependências
cargo update

# Mostrar árvore de dependências
cargo tree

Gerenciando Dependências

# Adicionar uma dependência (requer cargo-edit ou Cargo 1.62+)
cargo add serde --features derive
cargo add tokio --features full

# Adicionar dependência de desenvolvimento
cargo add --dev criterion

# Adicionar dependência de build
cargo add --build cc

# Remover uma dependência
cargo remove serde

# Ver dependências desatualizadas
cargo outdated  # requer cargo-outdated

Especificando versões no Cargo.toml:

[dependencies]
# Versão compatível (padrão) — aceita patches e minor updates
serde = "1.0"          # equivale a >=1.0.0, <2.0.0

# Versão exata
serde = "=1.0.193"

# Intervalo de versões
rand = ">=0.8, <0.9"

# Qualquer versão
log = "*"

# Dependência do Git
minha-crate = { git = "https://github.com/user/repo", branch = "main" }
minha-crate = { git = "https://github.com/user/repo", rev = "abc123" }

# Dependência local (path)
minha-lib = { path = "../minha-lib" }

# Dependência opcional
serde_yaml = { version = "0.9", optional = true }

Recursos Avançados

Workspaces

Workspaces permitem gerenciar múltiplos pacotes relacionados em um único repositório:

# Cargo.toml na raiz do workspace
[workspace]
members = [
    "crates/core",
    "crates/api",
    "crates/cli",
    "crates/utils",
]
resolver = "2"

# Dependências compartilhadas pelo workspace
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
anyhow = "1.0"
thiserror = "1.0"

# Metadados compartilhados
[workspace.package]
version = "0.1.0"
edition = "2021"
authors = ["Equipe <equipe@empresa.com>"]
license = "MIT"

Nos pacotes individuais, herde do workspace:

# crates/api/Cargo.toml
[package]
name = "meu-projeto-api"
version.workspace = true
edition.workspace = true
authors.workspace = true

[dependencies]
serde.workspace = true
tokio.workspace = true
meu-projeto-core = { path = "../core" }

Comandos para workspaces:

# Compilar todo o workspace
cargo build --workspace

# Compilar apenas um pacote
cargo build -p meu-projeto-api

# Testar todo o workspace
cargo test --workspace

# Executar um binário específico
cargo run -p meu-projeto-cli

Build Profiles

Customize como o Cargo compila seu código:

# Perfil de desenvolvimento (cargo build)
[profile.dev]
opt-level = 0          # Sem otimização (compilação rápida)
debug = true           # Informações de debug completas
debug-assertions = true
overflow-checks = true
incremental = true     # Compilação incremental
codegen-units = 256    # Mais unidades = compilação mais rápida

# Perfil de release (cargo build --release)
[profile.release]
opt-level = 3          # Otimização máxima
debug = false          # Sem informações de debug
debug-assertions = false
overflow-checks = false
lto = "fat"            # Link-Time Optimization completo
codegen-units = 1      # Menos unidades = código mais otimizado
panic = "abort"        # Menos código gerado para panics
strip = true           # Remove símbolos do binário

# Perfil customizado para testes de performance
[profile.bench]
opt-level = 3
debug = true           # Debug info para profiling

# Perfil customizado (Rust 1.57+)
[profile.profiling]
inherits = "release"
debug = true           # Release com debug info para profiling
strip = false

# Otimizar dependências mesmo em modo debug
[profile.dev.package."*"]
opt-level = 2

Features Flags e Compilação Condicional

Features permitem compilação condicional de funcionalidades:

# Cargo.toml
[features]
# Feature padrão (ativada por padrão)
default = ["json", "logging"]

# Features individuais
json = ["dep:serde_json"]
yaml = ["dep:serde_yaml"]
logging = ["dep:tracing"]
full = ["json", "yaml", "logging", "async"]
async = ["dep:tokio"]

# Feature que ativa feature de dependência
derive = ["serde/derive"]

[dependencies]
serde = "1.0"
serde_json = { version = "1.0", optional = true }
serde_yaml = { version = "0.9", optional = true }
tracing = { version = "0.1", optional = true }
tokio = { version = "1", features = ["full"], optional = true }

Usando features no código:

// src/lib.rs

// Módulo disponível apenas com a feature "json"
#[cfg(feature = "json")]
pub mod json {
    use serde_json;

    /// Serializa um valor para JSON
    pub fn para_json<T: serde::Serialize>(valor: &T) -> Result<String, serde_json::Error> {
        serde_json::to_string_pretty(valor)
    }

    /// Desserializa JSON para um tipo
    pub fn de_json<'a, T: serde::Deserialize<'a>>(json: &'a str) -> Result<T, serde_json::Error> {
        serde_json::from_str(json)
    }
}

// Módulo disponível apenas com a feature "yaml"
#[cfg(feature = "yaml")]
pub mod yaml {
    use serde_yaml;

    pub fn para_yaml<T: serde::Serialize>(valor: &T) -> Result<String, serde_yaml::Error> {
        serde_yaml::to_string(valor)
    }
}

// Logging condicional
#[cfg(feature = "logging")]
pub fn inicializar_logs() {
    tracing_subscriber::fmt::init();
}

#[cfg(not(feature = "logging"))]
pub fn inicializar_logs() {
    // Sem operação quando logging está desativado
}

// Compilação condicional por plataforma
#[cfg(target_os = "linux")]
pub fn caminho_config() -> &'static str {
    "/etc/meu-app/config.toml"
}

#[cfg(target_os = "windows")]
pub fn caminho_config() -> &'static str {
    "C:\\ProgramData\\meu-app\\config.toml"
}

#[cfg(target_os = "macos")]
pub fn caminho_config() -> &'static str {
    "/Library/Application Support/meu-app/config.toml"
}

Compilando com features:

# Compilar com features padrão
cargo build

# Compilar sem features padrão
cargo build --no-default-features

# Compilar com features específicas
cargo build --features "json,yaml"

# Compilar com todas as features
cargo build --all-features

Build Scripts (build.rs)

Build scripts executam antes da compilação:

// build.rs
use std::env;
use std::fs;
use std::path::Path;

fn main() {
    // Informar ao Cargo para re-executar se estes arquivos mudarem
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-changed=proto/api.proto");
    println!("cargo:rerun-if-env-changed=MEU_APP_VERSION");

    // Definir variáveis de ambiente acessíveis em compile-time
    let versao = env::var("MEU_APP_VERSION").unwrap_or_else(|_| "dev".to_string());
    println!("cargo:rustc-env=APP_VERSION={}", versao);

    // Gerar código
    let out_dir = env::var("OUT_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join("generated.rs");
    fs::write(
        &dest_path,
        "pub const BUILD_TIME: &str = \"2024-01-01T00:00:00Z\";\n",
    )
    .unwrap();

    // Linkar bibliotecas C
    println!("cargo:rustc-link-lib=static=minha_lib_c");
    println!("cargo:rustc-link-search=native=/usr/local/lib");

    // Passar flags para o compilador
    println!("cargo:rustc-cfg=feature_personalizada");
}

Usando código gerado:

// src/main.rs

// Incluir código gerado pelo build script
include!(concat!(env!("OUT_DIR"), "/generated.rs"));

fn main() {
    // Variável definida no build.rs
    println!("Versão: {}", env!("APP_VERSION"));
    println!("Build time: {}", BUILD_TIME);
}

Publicando no crates.io

# Fazer login no crates.io
cargo login seu-token-api

# Verificar o pacote antes de publicar
cargo package --list  # lista arquivos incluídos
cargo publish --dry-run  # simula publicação

# Publicar!
cargo publish

# Publicar com verificações ignoradas (use com cuidado)
cargo publish --no-verify

# Yank (despublicar) uma versão
cargo yank --version 0.1.0
cargo yank --version 0.1.0 --undo  # desfazer

Configurando o que é incluído no pacote:

[package]
# Incluir apenas estes arquivos
include = [
    "src/**/*",
    "Cargo.toml",
    "LICENSE",
    "README.md",
]

# Ou excluir arquivos específicos
exclude = [
    "tests/fixtures/*",
    ".github/*",
    "*.log",
]

Boas Práticas

1. Sempre Commite o Cargo.lock para Binários

# Para projetos binários: COMMITE o Cargo.lock
# Garante builds reproduzíveis

# Para bibliotecas: NÃO commite o Cargo.lock
# Adicione ao .gitignore
echo "Cargo.lock" >> .gitignore

2. Use Workspace Dependencies

# Evite duplicação de versões no workspace
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }

# Em cada crate do workspace:
[dependencies]
serde.workspace = true

3. Minimize Dependencies

// Prefira usar a stdlib quando possível
// Em vez de adicionar uma crate para isso:
use std::collections::HashMap;
use std::fs;
use std::io::{self, Read, Write};
use std::path::PathBuf;

4. Use cargo-deny para Auditoria

# deny.toml
[advisories]
vulnerability = "deny"
unmaintained = "warn"

[licenses]
allow = ["MIT", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause"]
confidence-threshold = 0.8

[bans]
multiple-versions = "warn"
wildcards = "deny"

[sources]
unknown-registry = "deny"
unknown-git = "deny"

5. Configure Aliases Úteis

# .cargo/config.toml
[alias]
br = "build --release"
t = "test"
tw = "test --workspace"
rr = "run --release"
cl = "clippy --all-targets --all-features -- -D warnings"
fmt-check = "fmt --all -- --check"

[build]
# Usar linker mais rápido (instale com: cargo install lld)
# Linux
rustflags = ["-C", "link-arg=-fuse-ld=lld"]
# macOS (requer: brew install llvm)
# rustflags = ["-C", "link-arg=-fuse-ld=/opt/homebrew/opt/llvm/bin/ld64.lld"]

# Número de jobs paralelos
jobs = 8

[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=lld"]

Exemplos Práticos

Exemplo 1: Projeto Completo com Múltiplas Crates

meu-saas/
├── Cargo.toml                 # Workspace root
├── crates/
│   ├── core/
│   │   ├── Cargo.toml
│   │   └── src/lib.rs         # Lógica de domínio
│   ├── api/
│   │   ├── Cargo.toml
│   │   └── src/main.rs        # Servidor HTTP
│   ├── worker/
│   │   ├── Cargo.toml
│   │   └── src/main.rs        # Processamento em background
│   └── cli/
│       ├── Cargo.toml
│       └── src/main.rs        # Ferramenta de administração
└── shared/
    ├── Cargo.toml
    └── src/lib.rs             # Tipos e utilitários compartilhados
# Cargo.toml (raiz)
[workspace]
members = [
    "crates/*",
    "shared",
]
resolver = "2"

[workspace.package]
version = "0.1.0"
edition = "2021"
license = "MIT"
rust-version = "1.75"

[workspace.dependencies]
# Dependências compartilhadas com versões centralizadas
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
anyhow = "1.0"
thiserror = "1.0"
uuid = { version = "1", features = ["v4", "serde"] }
chrono = { version = "0.4", features = ["serde"] }

Exemplo 2: Cargo.toml Completo para uma API

# crates/api/Cargo.toml
[package]
name = "meu-saas-api"
version.workspace = true
edition.workspace = true
license.workspace = true

[[bin]]
name = "api-server"
path = "src/main.rs"

[dependencies]
# Do workspace
serde.workspace = true
serde_json.workspace = true
tokio.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
anyhow.workspace = true
uuid.workspace = true
chrono.workspace = true

# Específicas desta crate
axum = { version = "0.7", features = ["macros"] }
tower = { version = "0.4", features = ["full"] }
tower-http = { version = "0.5", features = ["cors", "trace", "compression-gzip"] }
sqlx = { version = "0.7", features = ["runtime-tokio", "postgres", "chrono", "uuid"] }

# Dependências internas
meu-saas-core = { path = "../core" }
shared = { path = "../../shared" }

[dev-dependencies]
reqwest = { version = "0.11", features = ["json"] }
wiremock = "0.5"
testcontainers = "0.15"

[features]
default = ["postgres"]
postgres = ["sqlx/postgres"]
mysql = ["sqlx/mysql"]

Exemplo 3: Script de CI/CD com Cargo

# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy, rustfmt

      # Cache de dependências
      - uses: Swatinem/rust-cache@v2

      # Verificar formatação
      - run: cargo fmt --all -- --check

      # Linting com Clippy
      - run: cargo clippy --workspace --all-targets --all-features -- -D warnings

      # Compilar
      - run: cargo build --workspace --all-features

      # Testes
      - run: cargo test --workspace --all-features

      # Auditoria de segurança
      - run: cargo install cargo-audit && cargo audit

Comparação com Alternativas

CaracterísticaCargo (Rust)npm (Node.js)pip (Python)Maven (Java)
Gerenciamento de depsIntegradoIntegradoBásicoIntegrado
Build systemIntegradoScriptsExternoIntegrado
LockfileCargo.lockpackage-lock.jsonrequirements.txtpom.xml
ReprodutibilidadeExcelenteBoaFracaBoa
WorkspacesNativoNativoNãoMulti-módulo
TestesIntegradoExterno (jest)Externo (pytest)Integrado
DocsIntegradoExterno (jsdoc)Externo (sphinx)Integrado
BenchmarksIntegradoExternoExternoExterno
Compilação condicionalFeaturesNãoNãoProfiles
Velocidade de resoluçãoRápidaMédiaLentaMédia
Registrocrates.ionpmjs.comPyPIMaven Central

Plugins Essenciais do Cargo

# Instalar plugins úteis
cargo install cargo-watch    # Recompilar ao salvar
cargo install cargo-expand   # Expandir macros
cargo install cargo-audit    # Auditoria de segurança
cargo install cargo-outdated # Ver deps desatualizadas
cargo install cargo-deny     # Políticas de dependências
cargo install cargo-flamegraph # Gráficos de performance
cargo install cargo-nextest  # Test runner mais rápido
cargo install cargo-machete  # Encontrar deps não usadas
cargo install cargo-bloat    # Análise de tamanho do binário

# Usar plugins
cargo watch -x check -x test    # Checar e testar ao salvar
cargo expand nome_do_modulo      # Ver macro expandida
cargo audit                      # Checar vulnerabilidades
cargo outdated                   # Listar deps desatualizadas
cargo deny check                 # Verificar políticas
cargo flamegraph                 # Gerar flamegraph
cargo nextest run                # Testes paralelos rápidos
cargo machete                    # Deps não utilizadas
cargo bloat --release --crates   # Tamanho por crate

Conclusão

O Cargo é uma ferramenta indispensável no ecossistema Rust, combinando gerenciamento de dependências, build system, test runner e muito mais em uma única ferramenta coesa. Dominar o Cargo significa dominar o fluxo de trabalho Rust.

Pontos-chave para lembrar:

  • Cargo.toml é o centro de configuração do seu projeto
  • Workspaces organizam projetos complexos com múltiplas crates
  • Features permitem compilação condicional flexível
  • Build profiles otimizam para desenvolvimento ou produção
  • Plugins estendem funcionalidades com ferramentas da comunidade
  • Cargo.lock garante builds reproduzíveis para binários

Para aprofundar seus conhecimentos, consulte a documentação oficial do Cargo e explore o The Cargo Book para referência completa de todas as opções disponíveis.

No próximo passo, aprenda sobre o Rustup para gerenciar toolchains e targets de compilação.