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. Se você chegou pesquisando Cargo Rust, pense nesta página como um mapa completo: o que o Cargo faz, quais comandos usar no dia a dia, como organizar projetos reais e quais decisões evitam dor de cabeça em produção.

Cargo Rust em 2026: por que esta ferramenta importa

Cargo é uma das razões pelas quais Rust parece mais coeso que muitos ecossistemas de linguagens de sistemas. Em C e C++, cada projeto costuma escolher entre CMake, Make, Ninja, Conan, vcpkg, scripts próprios e convenções locais. Em Rust, a resposta padrão é Cargo. Isso reduz atrito para quem aprende, para quem entra em uma empresa nova e para quem precisa compilar projetos open source de terceiros.

A força do Cargo aparece em três frentes. A primeira é o fluxo de desenvolvimento: cargo new, cargo check, cargo test, cargo run e cargo build --release cobrem a maior parte da rotina. A segunda é reprodutibilidade: o Cargo.toml descreve intenção, enquanto o Cargo.lock fixa versões concretas para aplicações. A terceira é escala: workspaces, features, profiles e build scripts permitem sair de um CLI simples para uma plataforma com API, worker, biblioteca compartilhada e múltiplos binários.

Para quem está estudando Rust com foco em carreira, dominar Cargo vale tanto quanto memorizar sintaxe. Vagas que pedem Rust raramente querem apenas ownership e borrowing; elas esperam que você saiba criar projeto, adicionar dependência, configurar testes, separar crates, lidar com features opcionais, ler erros de build e publicar artefatos. Depois de passar pelos fundamentos em Aprenda Rust, Cargo é o próximo instrumento de produtividade.

Resposta rápida: principais comandos Cargo

ComandoQuando usar
cargo new meu-appCriar um projeto Rust novo
cargo initTransformar uma pasta existente em projeto Cargo
cargo checkValidar tipos rapidamente durante o desenvolvimento
cargo buildCompilar em modo debug
cargo build --releaseGerar binário otimizado para produção
cargo runCompilar e executar o binário principal
cargo testRodar testes unitários e de integração
cargo fmtFormatar o código com rustfmt
cargo clippyRodar lints do Clippy
cargo doc --openGerar documentação local
cargo treeInspecionar árvore de dependências

Se você só memorizar uma rotina inicial, use: cargo check enquanto escreve, cargo test antes de commitar, cargo clippy para pegar problemas idiomáticos e cargo build --release quando precisar medir performance real. Para APIs web com Axum e Tokio, essa disciplina economiza horas de debugging.

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 <[email protected]>"]
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 <[email protected]>"]
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

Cargo.toml vs Cargo.lock

Uma dúvida comum em buscas por Cargo Rust é a diferença entre Cargo.toml e Cargo.lock. O Cargo.toml é o manifesto escrito por você: nome do pacote, versão, edition, dependências, features, binários, workspaces e profiles. O Cargo.lock é gerado pelo Cargo: ele registra as versões exatas resolvidas para aquela compilação.

Em aplicações, CLIs, serviços web e workers, versionar Cargo.lock é quase sempre o caminho certo. Se você publica um binário ou roda um serviço em produção, quer que o deploy de amanhã use as mesmas versões que passaram nos testes hoje. Em bibliotecas, a decisão é mais contextual: algumas equipes não versionam o lockfile para garantir que a crate continue funcionando com todo o intervalo semântico declarado no Cargo.toml.

Esse detalhe parece burocrático, mas afeta segurança e confiabilidade. Ao combinar Cargo.lock, cargo audit, cargo deny e revisão de dependências, você reduz surpresas em projetos maiores. Veja também Clippy para lints de código e Rustfmt para padronização automática.

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",
]

Fluxo recomendado para projetos reais

Para um projeto pequeno, a sequência é simples: crie com cargo new, escreva código em src/main.rs ou src/lib.rs, rode cargo check, adicione testes e finalize com cargo test. Para um projeto profissional, acrescente algumas camadas desde cedo:

  1. Configure rust-toolchain.toml quando o time precisa fixar versão de compilador.
  2. Use cargo fmt --all -- --check e cargo clippy --all-targets --all-features -- -D warnings no CI.
  3. Separe domínio, API e workers em crates quando o código começar a crescer.
  4. Use features para dependências opcionais, não para esconder comportamento crítico.
  5. Meça release builds, não debug builds, antes de concluir que Rust está lento.
  6. Audite dependências com cargo audit ou cargo deny.

Esse fluxo conversa com vários caminhos do site: quem está construindo API deve ler Axum, SQLx e Tracing; quem está criando CLI deve ver Clap e o projeto prático de CLI com Clap; quem quer carreira deve conectar isso com vagas Rust e empresas que usam Rust.

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

Se você vem do ecossistema Go, vai notar que o sistema de Go Modules segue uma filosofia diferente — versões semânticas importadas diretamente pela URL do repositório. Já a linguagem Zig adota uma abordagem ainda mais radical com seu build system integrado, que serve tanto para compilar projetos Zig quanto para compilar código C/C++ sem dependências externas.

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.