---
title: "cargo-bloat: Reduza Binários Rust | Rust Brasil"
url: "https://rustlang.com.br/blog/cargo-bloat-reduzir-tamanho-binario-rust-2026/"
markdown_url: "https://rustlang.com.br/blog/cargo-bloat-reduzir-tamanho-binario-rust-2026.MD"
description: "Use cargo-bloat para descobrir quais funções e crates aumentam seu binário Rust. Guia com release, features, LTO, strip, CI e checklist de otimização."
date: "2026-08-06"
author: "Equipe Rust Brasil"
---

# cargo-bloat: Reduza Binários Rust | Rust Brasil

Use cargo-bloat para descobrir quais funções e crates aumentam seu binário Rust. Guia com release, features, LTO, strip, CI e checklist de otimização.


**O `cargo-bloat` é a ferramenta certa quando um binário Rust ficou grande e você precisa descobrir, em vez de adivinhar, quais funções e dependências estão ocupando espaço.** Instale com `cargo install cargo-bloat --locked`, compile o perfil que realmente será distribuído e rode `cargo bloat --release --crates`. O relatório agrupa a seção de código por crate; depois, `cargo bloat --release -n 20` mostra as funções mais pesadas para orientar o próximo experimento.

O ponto importante é que o `cargo-bloat` **não emagrece o executável sozinho**. Ele funciona como um profiler de tamanho: revela onde os bytes estão e permite testar, com comparação antes/depois, decisões como remover features padrão, ativar LTO, trocar uma dependência pesada ou separar uma funcionalidade opcional.

Este guia mostra como analisar tamanho de binário Rust com `cargo-bloat`, interpretar as colunas do relatório, investigar features e monomorfização, configurar um profile enxuto e evitar otimizações que economizam poucos megabytes enquanto criam risco operacional.

## Quando o tamanho do binário Rust importa

Um executável maior nem sempre é um problema. Para uma API em uma VPS com vários gigabytes de disco, reduzir um artefato de 32 MB para 20 MB pode não mudar nada para o usuário. O tamanho passa a ser requisito quando afeta uma parte mensurável da entrega:

- **CLIs distribuídas pela internet:** downloads menores melhoram instalação e atualização;
- **containers:** artefatos menores reduzem transferência entre registry, runner e cluster;
- **serverless e edge:** pacote e cold start podem ter limites apertados;
- **embedded:** flash e memória são restrições físicas;
- **aplicativos desktop:** instalador e atualizações diferenciais pesam na experiência;
- **agentes e sidecars:** dezenas de cópias multiplicam armazenamento e tráfego;
- **supply chain:** menos dependências e features podem reduzir superfície de manutenção, embora tamanho não seja prova de segurança.

Antes de otimizar, registre pelo menos quatro números: tamanho do arquivo, tamanho compactado, tempo de inicialização e performance da carga principal. Isso evita uma vitória falsa, como reduzir 8 MB e aumentar o uso de CPU em produção.

Para investigar CPU e memória em runtime, o caminho é outro: veja o guia de [profiling de Rust em produção](/blog/rust-profiling-performance-producao-2026/). O `cargo-bloat` responde principalmente a **quem ocupa espaço no executável**.

## O que é cargo-bloat

O [`cargo-bloat`](https://github.com/RazrFalcon/cargo-bloat) é um subcomando do Cargo que examina o artefato compilado e relaciona partes da seção de texto — o código de máquina — a símbolos e crates. Na prática, ele ajuda a responder:

1. quais funções geram mais código;
2. quais crates respondem pela maior parte da seção analisada;
3. se uma mudança de feature, profile ou dependência realmente reduziu o artefato;
4. se o crescimento veio do código da aplicação ou de uma biblioteca transitiva.

A ferramenta não mede tudo da mesma forma que `du` ou `ls -lh`. Um executável também pode conter metadados, símbolos, tabelas, dados estáticos, alinhamento e informações de depuração. Por isso, a soma exibida no relatório pode não ser igual ao tamanho total do arquivo.

Essa diferença não é defeito. Use cada medida para sua pergunta:

| Medida | Pergunta respondida |
|---|---|
| `ls -lh target/release/app` | Qual é o tamanho do arquivo entregue? |
| `gzip -9 -c app \| wc -c` | Quanto custa aproximadamente transferir o artefato comprimido? |
| `cargo bloat --release --crates` | Quais crates ocupam mais código? |
| `cargo bloat --release -n 20` | Quais funções aparecem entre as maiores? |
| `strip` / `profile.release.strip` | Quanto some ao remover símbolos desnecessários? |

## Instalação e primeira análise

Instale a ferramenta usando o próprio Cargo:

```bash
cargo install cargo-bloat --locked
cargo bloat --version
```

Na raiz do projeto, gere uma referência do artefato final:

```bash
cargo build --release --locked
ls -lh target/release/minha-aplicacao
cargo bloat --release
```

Para ver as crates em vez de uma lista longa de funções:

```bash
cargo bloat --release --crates
```

Para limitar a saída às vinte funções mais relevantes:

```bash
cargo bloat --release -n 20
```

Se o workspace tem vários binários, informe o alvo de forma explícita:

```bash
cargo bloat --release --bin minha-api --crates
cargo bloat --release --bin meu-worker -n 30
```

Isso é essencial em [Cargo workspaces e monorepos](/blog/cargo-workspaces-monorepos-rust-2026/). Comparar a API em um comando e o worker em outro pode revelar que os dois carregam features diferentes da mesma dependência.

### Guarde uma baseline reproduzível

Não compare um build `debug` com um `release`, nem um build limpo com outro que usa flags diferentes. Registre:

```bash
rustc --version --verbose
cargo --version
git rev-parse HEAD
cargo bloat --release --bin minha-api --crates > /tmp/bloat-antes.txt
```

Depois da alteração:

```bash
cargo bloat --release --bin minha-api --crates > /tmp/bloat-depois.txt
diff -u /tmp/bloat-antes.txt /tmp/bloat-depois.txt
```

O ideal é repetir a análise com o mesmo `Cargo.lock`, target, profile e toolchain. Se um lado usa `x86_64-unknown-linux-gnu` e o outro usa `musl`, você está comparando produtos diferentes.

## Como interpretar o relatório

Uma saída do `cargo-bloat` normalmente inclui tamanho do arquivo, tamanho da seção de texto e linhas atribuídas a símbolos. Os nomes exatos e o formato podem variar conforme a versão e a plataforma, mas a leitura operacional é estável.

### File

É a quantidade de bytes associada àquele símbolo no arquivo analisado. Ajuda a ordenar os maiores contribuintes visíveis.

### .text

Representa a participação na seção de código executável. É útil para entender quanto código de máquina foi gerado, mas não inclui todos os componentes do arquivo final.

### Crate ou symbol name

No modo `--crates`, mostra a atribuição agrupada por crate. No modo padrão, mostra funções e símbolos — frequentemente com nomes longos por causa de genéricos, closures e monomorfizações.

Não conclua que a primeira crate da lista é “ruim”. Uma runtime async, um parser ou uma biblioteca criptográfica pode ser grande porque oferece a função central do produto. A pergunta correta é: **estamos usando essa capacidade, e ela precisa estar neste binário?**

## Primeiro alvo: features desnecessárias

O maior ganho de baixo risco costuma vir de features. Muitas crates ativam um conjunto conveniente por padrão, mas seu binário usa apenas uma parte.

Considere uma dependência HTTP hipotética:

```toml
[dependencies]
reqwest = "0.12"
```

Se o projeto só precisa de JSON e TLS com rustls, uma configuração mais explícita pode ser:

```toml
[dependencies]
reqwest = {
  version = "0.12",
  default-features = false,
  features = ["json", "rustls-tls"]
}
```

O resultado depende do grafo real. O procedimento seguro é:

1. rode `cargo tree -e features`;
2. identifique features padrão e quem as ativa;
3. desative apenas o que entende;
4. compile e teste todos os targets relevantes;
5. compare `cargo bloat --release --crates` e o tamanho final;
6. execute benchmarks e testes de integração.

Use também:

```bash
cargo tree -e features -i nome-da-crate
```

O `-i` ajuda a descobrir por que uma crate aparece no grafo. Às vezes você remove uma dependência direta, mas outra biblioteca continua puxando a mesma crate transitivamente.

Essa revisão conversa com o guia de [Cargo e suas ferramentas essenciais](/artigos/cargo-ferramentas-essenciais/) e com práticas de [segurança da supply chain em Rust](/blog/rust-seguranca-supply-chain-cargo-deny-sbom-2026/): um grafo menor pode ser mais simples de atualizar e auditar, mas a decisão precisa preservar funcionalidade.

## Segundo alvo: profile de release

Depois de cortar features obviamente desnecessárias, ajuste o profile. Uma base comum para priorizar tamanho é:

```toml
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
strip = "symbols"
```

Cada opção tem custo e precisa ser medida.

### `opt-level = "z"`

Pede otimização agressiva para tamanho. Em algumas cargas, `"s"` ou o padrão de release produz melhor equilíbrio entre velocidade e bytes. Faça benchmark; não existe vencedor universal.

### `lto = true`

Link-Time Optimization permite otimizações entre crates e pode remover código ou chamadas redundantes. O custo costuma ser build e link mais lentos. Em CI, combine a decisão com [sccache](/blog/sccache-rust-cache-compilacao-ci-2026/) e com o guia de [tempo de compilação em Rust](/blog/rust-tempo-compilacao-otimizar-build-2026/).

### `codegen-units = 1`

Dá ao compilador mais contexto para otimização, mas reduz paralelismo e aumenta o tempo de compilação. É uma escolha de release, não necessariamente de desenvolvimento local.

### `panic = "abort"`

Remove a infraestrutura de unwind em cenários compatíveis e pode reduzir o binário. Em troca, o processo encerra ao ocorrer panic. Bibliotecas, FFI e aplicações que dependem de `catch_unwind` exigem análise especial.

### `strip = "symbols"`

Remove símbolos que não precisam acompanhar o artefato distribuído. Antes de ativar, defina como sua equipe simboliza crashes e perfis. Uma prática madura é guardar símbolos separadamente como artefato privado da release, em vez de simplesmente apagá-los sem plano de diagnóstico.

## Monomorfização: quando um genérico vira muitas cópias

Rust especializa funções genéricas para tipos concretos. Isso entrega performance sem dispatch dinâmico, mas pode gerar várias versões de uma função.

Exemplo simplificado:

```rust
fn serializar<T: serde::Serialize>(valor: &T) -> Vec<u8> {
    serde_json::to_vec(valor).expect("valor serializável")
}
```

Se o binário chama essa função para muitos tipos, o compilador pode gerar especializações. O relatório pode exibir nomes parecidos repetidos com parâmetros diferentes.

Não remova genéricos por reflexo. Alternativas como trait objects, enums ou uma camada não genérica podem reduzir código e também piorar performance, ergonomia ou arquitetura. Primeiro confirme que as múltiplas instâncias ocupam espaço relevante.

Quando a suspeita é monomorfização em escala, o `cargo-llvm-lines` é um complemento útil: ele ajuda a estimar quanto LLVM IR cada função gera. A divisão prática é:

- **cargo-bloat:** onde estão os bytes no executável;
- **cargo-llvm-lines:** onde o compilador está gerando muito código intermediário;
- **benchmark/profiler:** qual impacto a mudança produz em runtime.

## Dependência pesada: trocar, configurar ou aceitar?

Depois do relatório, uma crate pode dominar o tamanho. Existem quatro respostas legítimas:

1. **aceitar:** ela implementa uma capacidade central e o tamanho cabe no orçamento;
2. **configurar:** desligar features ou escolher um backend menor;
3. **isolar:** mover uma função rara para outro binário, plugin ou serviço;
4. **substituir:** adotar uma alternativa menor após comparar manutenção e comportamento.

Nunca faça “dependency golf” apenas para ganhar um gráfico. Avalie:

- maturidade e frequência de atualizações;
- histórico de segurança;
- compatibilidade de licença;
- suporte a targets necessários;
- performance e alocações;
- custo de migração;
- qualidade da API e da documentação.

Em uma API [Axum](/ecossistema/axum/), por exemplo, remover a runtime [Tokio](/ecossistema/tokio/) não é uma otimização realista se todo o serviço depende dela. Já desativar features de protocolos ou codecs que não são usados pode ser um ganho honesto.

## cargo-bloat em Docker, musl e cross-compile

Analise o mesmo tipo de artefato que vai para produção. Se você distribui `x86_64-unknown-linux-musl`, não baseie a decisão apenas no build GNU local.

Com um target instalado, a chamada segue o padrão:

```bash
rustup target add x86_64-unknown-linux-musl
cargo bloat --release \
  --target x86_64-unknown-linux-musl \
  --bin minha-api \
  --crates
```

O ambiente ainda precisa do linker e das bibliotecas corretas. Para pipelines multiplataforma, consulte o guia de [cargo-zigbuild e cross-compilation](/blog/cargo-zigbuild-cross-compile-rust-2026/).

Em Docker multi-stage, compare duas coisas:

1. o binário produzido pelo builder;
2. a imagem final publicada.

Uma imagem de 900 MB pode continuar enorme mesmo com executável enxuto porque a stage final copiou toolchain, cache ou pacotes desnecessários. Use [builds Docker otimizados para Rust](/blog/rust-docker-builds-otimizados-producao-2026/) e [cargo-chef](/blog/cargo-chef-cache-docker-rust-2026/) para resolver a estrutura do container; use `cargo-bloat` para investigar o código do executável.

## Como colocar um orçamento de tamanho na CI

Não comece bloqueando toda pull request por uma variação de poucos bytes. Compiladores, versões de dependência e linkers podem mudar o resultado. Uma adoção progressiva funciona melhor:

1. gere o relatório em releases ou em um job noturno;
2. publique tamanho do arquivo, tamanho comprimido e top crates como artefato;
3. observe algumas semanas para entender a variação normal;
4. defina um orçamento alinhado ao produto;
5. bloqueie apenas regressões relevantes e revisáveis.

Exemplo simples para medir o arquivo:

```bash
cargo build --release --locked --bin minha-api
bytes=$(stat -c%s target/release/minha-api)
limite=$((25 * 1024 * 1024))

printf 'binário: %s bytes\n' "$bytes"

if [ "$bytes" -gt "$limite" ]; then
  echo "O binário ultrapassou o orçamento de 25 MiB"
  exit 1
fi
```

Para investigação, salve também:

```bash
cargo bloat --release --bin minha-api --crates \
  > cargo-bloat-crates.txt
```

O número de 25 MiB acima é apenas exemplo. Um firmware, uma CLI e um backend têm orçamentos completamente diferentes. Documente **por que** o limite existe e quem pode aprovar uma exceção.

## Erros comuns ao otimizar tamanho

### Medir apenas o build debug

Builds de desenvolvimento contêm decisões diferentes e não representam o artefato distribuído. Use o profile e o target reais.

### Confundir `strip` com remoção de código

`strip` pode reduzir bastante o arquivo ao remover símbolos, mas não significa que você eliminou dependências ou lógica. Compare o arquivo e a seção de código.

### Remover capacidade sem teste de integração

Desligar uma feature pode compilar e ainda quebrar TLS, compressão, banco de dados ou observabilidade em produção. Teste o caminho completo.

### Priorizar bytes e ignorar CPU

`opt-level = "z"` pode economizar espaço e piorar throughput. Rode benchmark da carga principal e meça cold start separadamente.

### Comparar targets ou toolchains diferentes

Uma atualização de Rust, LLVM, linker ou libc pode alterar o layout. Para atribuir causalidade, mude uma variável por vez.

### Otimizar sem orçamento

Sem requisito, qualquer tamanho parece grande. Defina o objetivo: download, flash, cold start, imagem ou custo de distribuição.

## Checklist prático de redução

- [ ] medir o binário `--release` do target realmente distribuído;
- [ ] registrar `rustc`, `Cargo.lock`, commit e flags da baseline;
- [ ] rodar `cargo bloat --release --crates`;
- [ ] inspecionar as maiores funções com `-n`;
- [ ] revisar `cargo tree -e features`;
- [ ] remover apenas features comprovadamente desnecessárias;
- [ ] testar `opt-level = "s"` e `"z"` com benchmark;
- [ ] medir LTO e `codegen-units = 1` contra tempo de build;
- [ ] decidir conscientemente sobre `panic = "abort"`;
- [ ] manter uma estratégia para símbolos antes de usar `strip`;
- [ ] validar testes, startup, memória e throughput;
- [ ] colocar orçamento na CI só depois de conhecer a variação normal.

## cargo-bloat e carreira Rust

Saber usar `cargo-bloat` é um bom sinal em entrevistas de sistemas, embedded, plataforma e engenharia de performance porque demonstra um processo: medir, localizar, formular hipótese, alterar uma variável e validar regressões.

Uma resposta madura para “como reduziria um binário Rust?” não é “ativaria LTO”. É:

1. confirmaria o requisito e o target;
2. criaria baseline reproduzível;
3. usaria `cargo-bloat` e `cargo tree -e features`;
4. atacaria features e dependências sem função no produto;
5. compararia profiles;
6. validaria runtime e depuração;
7. automatizaria um orçamento razoável.

Esse tipo de experiência aparece em times que entregam CLIs, agentes, segurança, infraestrutura e [sistemas embarcados com Rust](/artigos/rust-para-embarcados/). Para acompanhar oportunidades, consulte as [vagas Rust](/vagas/) e as [empresas que usam Rust](/empresas/).

## Perguntas frequentes

### O que é cargo-bloat?

cargo-bloat é uma ferramenta do ecossistema Cargo que analisa o executável compilado e mostra quais funções ou crates ocupam mais espaço na seção de código. Ela ajuda a transformar a pergunta genérica sobre binário grande em uma lista concreta de candidatos para otimização.

### Como usar cargo-bloat em um projeto Rust?

Instale e rode:

```bash
cargo install cargo-bloat --locked
cargo bloat --release
cargo bloat --release --crates
cargo bloat --release -n 20
```

Em workspaces, informe `--bin`, `--example` ou outro alvo aplicável para analisar o artefato correto.

### cargo-bloat reduz o tamanho do binário automaticamente?

Não. A ferramenta apenas mede e atribui espaço a funções e crates. A redução vem depois, ao remover features desnecessárias, substituir dependências pesadas, ajustar o profile de release, ativar LTO, usar `panic = "abort"` ou separar funcionalidades quando isso fizer sentido para o produto.

### Qual é a diferença entre cargo-bloat, strip e cargo-llvm-lines?

cargo-bloat atribui bytes do executável a funções e crates; strip remove símbolos e informações que não precisam acompanhar o artefato final; cargo-llvm-lines ajuda a enxergar quanto código LLVM é gerado, especialmente por monomorfização. As ferramentas respondem a perguntas diferentes e podem ser combinadas.

### Devo escolher toda dependência pelo menor tamanho?

Não. Tamanho é apenas um requisito entre segurança, manutenção, performance, compatibilidade e produtividade. Otimize quando houver uma restrição real de distribuição, cold start, imagem, firmware ou armazenamento, e valide testes e benchmarks depois de cada mudança.

## Conclusão

O `cargo-bloat` substitui palpites por evidência. Em poucos comandos, ele mostra se o tamanho do executável está concentrado no código da aplicação, em uma dependência, em features que entraram por conveniência ou em várias especializações genéricas.

O fluxo recomendado é simples: **meça o release real, agrupe por crate, investigue funções, revise features, teste profiles e valide o produto inteiro**. LTO, `strip` e `panic = "abort"` são ferramentas; não são metas. A meta é entregar um artefato que caiba no orçamento sem perder segurança, observabilidade ou performance onde elas importam.

Depois de controlar o tamanho, conecte essa análise ao restante do pipeline: [sccache para cache de compilação](/blog/sccache-rust-cache-compilacao-ci-2026/), [mold para link mais rápido](/blog/mold-linker-rust-compilacao-rapida-2026/), [cargo-semver-checks para compatibilidade de API](/blog/cargo-semver-checks-rust-api-ci-2026/) e [release engineering em Rust](/blog/rust-release-engineering-binaries-cli-servicos-2026/). Um binário pequeno é útil; um binário pequeno, testado e reproduzível é um produto.
