Use cargo tree para enxergar o grafo real que o Cargo resolveu, incluindo dependências transitivas que não aparecem no seu Cargo.toml. Os três comandos mais úteis são cargo tree para ver a árvore completa, cargo tree -i nome-da-crate para descobrir quem introduziu um pacote e cargo tree -d para localizar versões duplicadas.
cargo tree
cargo tree -i serde
cargo tree -d
Essa inspeção responde perguntas que surgem em manutenção, segurança e performance: por que duas versões da mesma crate estão no Cargo.lock? Quem trouxe uma biblioteca vulnerável? Qual feature ativou um backend pesado? Por que o binário ganhou dependências depois de uma mudança pequena?
cargo tree não modifica o projeto. Ele transforma a resolução do Cargo em uma visão navegável para que você decida o que alterar com contexto.
Resposta rápida: comandos essenciais
| Objetivo | Comando |
|---|---|
| Ver a árvore de dependências | cargo tree |
| Descobrir quem depende de uma crate | cargo tree -i nome-da-crate |
| Investigar uma versão específica | cargo tree -i [email protected] |
| Mostrar versões duplicadas | cargo tree -d |
| Visualizar features ativadas | cargo tree -e features |
| Limitar a profundidade | cargo tree --depth 2 |
| Analisar um pacote do workspace | cargo tree -p nome-do-pacote |
| Considerar um target | cargo tree --target x86_64-unknown-linux-gnu |
| Evitar a compactação de repetições | cargo tree --no-dedupe |
Confira cargo tree --help na toolchain instalada antes de automatizar formatos ou opções em scripts críticos. O Cargo evolui, e uma esteira deve fixar a toolchain que validou.
O que o cargo tree mostra
Um projeto Rust pequeno pode declarar apenas duas dependências:
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
serde = { version = "1", features = ["derive"] }
Mas essas duas linhas podem resolver dezenas de crates transitivas para HTTP, TLS, serialização, parsing de URL, concorrência e integração com o sistema operacional. cargo tree apresenta esse resultado hierarquicamente:
meu-app v0.1.0
├── reqwest v0.12.x
│ ├── bytes v1.x
│ ├── http v1.x
│ ├── hyper v1.x
│ ├── serde v1.x
│ └── tokio v1.x
└── serde v1.x
A saída exata depende do manifesto, do lockfile, das features, da plataforma e da versão do toolchain. O ponto central é distinguir:
- dependência direta: declarada pelo seu pacote;
- dependência transitiva: trazida por outra crate;
- versão resolvida: versão concreta escolhida pelo resolvedor;
- feature ativada: capacidade opcional que pode acrescentar código e dependências;
- tipo de aresta: dependência normal, de desenvolvimento ou de build.
Para revisar como Cargo.toml, requisitos semânticos e Cargo.lock se relacionam, leia o guia de gerenciamento de dependências em Rust e a referência do Cargo.
Descobrindo quem trouxe uma crate com -i
O modo invertido é o recurso mais valioso no diagnóstico diário. Em vez de mostrar “esta crate depende daquelas”, ele responde “quem depende desta crate?”.
cargo tree -i openssl-sys
Uma saída conceitual poderia indicar:
openssl-sys v0.9.x
└── native-tls v0.2.x
└── hyper-tls v0.6.x
└── reqwest v0.12.x
└── meu-app v0.1.0
Agora existe um caminho causal: reqwest ativou uma integração que chegou a openssl-sys. Isso permite investigar se:
- a feature TLS escolhida é intencional;
- o projeto poderia usar outro backend;
- uma dependência direta deixou features padrão ligadas sem necessidade;
- o pacote aparece apenas em testes ou no build;
- a plataforma de deploy realmente precisa desse caminho.
O mesmo método ajuda em um advisory. Se cargo-audit apontar uma crate transitiva, não comece editando o Cargo.lock às cegas. Primeiro execute:
cargo tree -i crate-afetada
Depois identifique o mantenedor da aresta que você controla: sua dependência direta, uma feature, um pacote interno do workspace ou uma build dependency.
Quando existem várias versões
Se duas versões da crate estão resolvidas, informe a versão na consulta:
cargo tree -i [email protected]
cargo tree -i [email protected]
Isso separa os caminhos e evita atribuir a versão antiga ao pacote errado.
Encontrando versões duplicadas com -d
Execute:
cargo tree -d
O Cargo destacará crates presentes em múltiplas versões. Duplicidade não significa automaticamente defeito. Ela pode ser necessária quando duas dependências exigem intervalos incompatíveis. Ainda assim, merece investigação porque pode causar:
- compilação repetida de código semelhante;
- binários maiores;
- mais dependências para auditar;
- tipos incompatíveis entre versões da mesma crate;
- migrações mais difíceis;
- comportamento diferente em partes do sistema.
Considere este caso conceitual:
http v0.2.x
└── cliente-legado v1.x
http v1.x
└── reqwest v0.12.x
Não é possível “forçar” http v1 sobre cliente-legado se ele realmente depende da linha 0.2. A correção sustentável pode exigir atualizar ou substituir cliente-legado.
O fluxo recomendado é:
cargo tree -d
cargo tree -i [email protected]
cargo tree -i [email protected]
Depois verifique:
- há versão mais nova da dependência direta que converge o grafo?
- uma feature opcional introduz a linha antiga?
- a duplicidade existe apenas em dev-dependencies?
- os tipos das duas versões atravessam sua API pública?
- o custo aparece no artefato final ou somente no build?
Combine essa análise com cargo-outdated para encontrar atualizações disponíveis. Atualize em lotes pequenos, revise o Cargo.lock e rode os testes; não trate toda duplicidade como algo que precisa desaparecer no mesmo pull request.
Entendendo features com -e features
Features são aditivas na maior parte do ecossistema Cargo: quando caminhos diferentes ativam features da mesma crate, o conjunto efetivo tende a ser a união delas para aquela resolução. Isso surpreende quando uma integração aparentemente pequena liga uma árvore grande.
Use:
cargo tree -e features
A saída fica mais detalhada porque passa a mostrar arestas de features. Ela ajuda a responder:
- quem ativou
serde/derive? - por que uma crate está compilando suporte a TLS nativo?
- qual pacote habilitou macros ou runtime completo?
- uma feature padrão está adicionando dependências desnecessárias?
- o workspace unificou features que um pacote isolado não ativaria?
Para investigar um pacote específico em um workspace:
cargo tree -p minha-lib -e features
Se o relatório for grande, limite a profundidade ou procure o trecho relevante. Depois compare builds com e sem defaults:
cargo tree -p minha-lib -e features
cargo tree -p minha-lib -e features --no-default-features
A disponibilidade de argumentos de seleção de features acompanha os comandos Cargo; confirme a sintaxe na toolchain local. O objetivo é comparar configurações reais, não produzir uma árvore bonita que nenhum consumidor usa.
Para testar sistematicamente features isoladas e combinações, use cargo-hack. cargo tree explica a matriz; cargo-hack verifica se ela compila e funciona.
Workspaces: qual grafo você está analisando?
Em um workspace, a raiz pode conter bibliotecas, serviços, CLIs, ferramentas de migração e benchmarks. A árvore completa mistura responsabilidades e pode esconder o caminho relevante.
Restrinja por pacote:
cargo tree -p api
cargo tree -p worker
cargo tree -p minha-lib-publica
Compare os resultados para descobrir, por exemplo, por que o serviço HTTP depende de uma crate que a biblioteca pública não deveria expor.
Uma política útil é analisar separadamente:
- pacotes publicados no crates.io;
- aplicações entregues em produção;
- ferramentas internas;
- build dependencies e proc-macros;
- benchmarks e dependências de desenvolvimento.
O guia de Cargo workspaces e monorepos mostra como organizar manifestos compartilhados. A inspeção por pacote evita confundir “está no repositório” com “está neste artefato”.
Feature unification no workspace
Um pacote pode parecer enxuto quando compilado isoladamente e ganhar features quando o workspace inteiro é resolvido. Isso ocorre porque outro membro depende da mesma crate com capacidades adicionais.
Por isso, compare o contexto que importa para o consumidor:
cargo tree -p minha-lib -e features
cargo tree --workspace -e features
Não conclua que uma biblioteca publicada exige toda feature vista no build monolítico sem testar o pacote no contexto de publicação.
Targets e dependências condicionais
Manifestos podem declarar dependências por sistema operacional ou arquitetura:
[target.'cfg(unix)'.dependencies]
nix = "0.29"
[target.'cfg(windows)'.dependencies]
windows-sys = "0.59"
Uma árvore observada no Linux não representa necessariamente o build do Windows. Use --target para inspecionar a resolução associada ao alvo:
cargo tree --target x86_64-unknown-linux-gnu
cargo tree --target x86_64-pc-windows-msvc
cargo tree --target wasm32-unknown-unknown
O target talvez precise estar instalado para outras etapas do build, mas a inspeção já ajuda a encontrar dependências condicionais e diferenças de plataforma.
Isso é especialmente importante em cross-compilation com cargo-zigbuild, WebAssembly e embedded. Uma dependência válida no host pode ser incompatível com o alvo final.
Limitando e formatando relatórios grandes
Em projetos maduros, a árvore completa pode ter centenas de linhas. Comece com uma pergunta específica em vez de colar tudo em uma issue.
Limite a profundidade
cargo tree --depth 2
Isso oferece uma visão de alto nível das dependências mais próximas do pacote raiz.
Desative a deduplicação visual
O Cargo normalmente compacta repetições na apresentação para não imprimir a mesma subárvore indefinidamente. Quando você precisa ver cada caminho:
cargo tree --no-dedupe
O relatório cresce bastante. Use apenas durante uma investigação focada.
Restrinja o pacote
cargo tree -p meu-binario
Essa é quase sempre uma primeira redução melhor do que processar todo o workspace.
Use consultas invertidas
Quando você já conhece a crate problemática, -i é mais informativo do que uma busca textual na árvore completa:
cargo tree -i crate-problematica
cargo tree para reduzir binário e compilação
A presença no grafo não prova que uma crate domina o tamanho do binário, mas revela os caminhos que merecem medição. Um fluxo de performance pode ser:
cargo tree -dpara localizar duplicidades;cargo tree -e featurespara identificar integrações habilitadas;cargo tree -i crate-pesadapara encontrar a origem;- cargo-bloat para medir contribuição ao artefato;
- cargo-llvm-lines para investigar geração de código;
- benchmarks e medições antes de remover capacidades.
Evite a conclusão “muitas dependências são sempre ruins”. Uma crate bem mantida pode economizar código inseguro e trabalho operacional. O problema é depender sem saber por quê, ativar features inúteis ou carregar múltiplas linhas antigas por falta de manutenção.
cargo tree em segurança e supply chain
O grafo é a superfície real de terceiros do projeto, não apenas a lista curta em [dependencies]. Durante uma análise de supply chain, use cargo tree para:
- mapear a origem de uma crate com advisory;
- localizar proc-macros e build dependencies;
- entender se uma biblioteca entra apenas em desenvolvimento;
- investigar duplicidades de componentes sensíveis;
- validar a remoção de uma dependência;
- explicar por que uma atualização moveu várias transitivas.
Depois combine o mapa com ferramentas especializadas:
- cargo-audit para advisories RustSec;
- cargo-vet para auditorias e confiança;
- cargo-deny e SBOM para fontes, licenças, bans e inventário;
- cargo-machete e cargo-udeps para dependências diretas possivelmente sem uso.
Nenhuma dessas ferramentas substitui as outras. cargo tree mostra relações; scanners e políticas atribuem significado a elas.
Comparando o grafo antes e depois de uma mudança
Ao atualizar dependências, salve um relatório de referência:
cargo tree > /tmp/tree-antes.txt
cargo update -p alguma-crate
cargo tree > /tmp/tree-depois.txt
diff -u /tmp/tree-antes.txt /tmp/tree-depois.txt
O diff ajuda a detectar:
- novas dependências transitivas;
- remoções esperadas;
- convergência ou criação de versões duplicadas;
- troca de backend;
- alteração causada por features;
- atualização muito mais ampla do que o pull request sugeria.
Não trate o texto inteiro como artefato estável entre versões de toolchain. Para revisão humana dentro do mesmo ambiente, porém, ele é excelente.
Depois execute a validação normal:
cargo fmt --check
cargo check --workspace --all-targets
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
Se a mudança mexeu em features, expanda a matriz com cargo-hack. Se afetou performance ou tamanho, meça o resultado em vez de inferir pela quantidade de linhas da árvore.
Armadilhas comuns
Tentar editar uma dependência transitiva diretamente
O caminho sustentável geralmente passa por atualizar a dependência direta, mudar uma feature ou contribuir com o pacote intermediário. Alterar o lockfile manualmente não muda os requisitos do grafo.
Tratar toda duplicidade como bug
Intervalos incompatíveis podem exigir versões diferentes. Investigue custo e caminhos antes de forçar convergência.
Analisar apenas a árvore do host
Windows, Linux, macOS, WASM e targets embedded podem resolver conjuntos diferentes. Use o target correspondente ao artefato entregue.
Ignorar dev e build dependencies
Elas talvez não entrem no binário, mas executam ou compilam em partes importantes da esteira. Proc-macros e scripts de build merecem atenção de supply chain.
Confundir presença com impacto
Uma crate na árvore não necessariamente ocupa muito espaço nem está no caminho quente. Use ferramentas de medição para tamanho e profiling.
Rodar cargo tree sem uma pergunta
Uma árvore enorme impressiona, mas não produz decisão. Comece por “quem trouxe X?”, “por que existem duas versões de Y?” ou “qual feature ativou Z?”.
Checklist de diagnóstico
- executar
cargo treepara reconhecer o grafo; - restringir ao pacote relevante com
-p; - usar
-ipara encontrar a origem da crate investigada; - informar
crate@versãoquando houver múltiplas linhas; - rodar
cargo tree -de classificar duplicidades; - inspecionar features com
-e features; - comparar configurações com e sem defaults quando necessário;
- verificar targets diferentes para dependências condicionais;
- revisar o diff da árvore antes e depois de atualizações;
- medir tamanho e performance com ferramentas próprias;
- rodar auditorias de segurança sobre o
Cargo.lock; - registrar o caminho causal na issue ou pull request.
cargo tree para carreira e portfólio Rust
Em projetos de portfólio, demonstre que você entende o ecossistema além de adicionar crates. Uma seção de arquitetura pode documentar:
- por que determinado backend TLS foi escolhido;
- como as features mantêm a biblioteca enxuta;
- qual dependência direta introduz componentes críticos;
- como a CI detecta advisories e duplicidades relevantes;
- por que uma versão duplicada foi aceita temporariamente;
- como uma atualização reduziu o grafo ou o tempo de build.
Esse repertório aparece em trabalho de backend, plataforma, segurança, embedded, WebAssembly, bibliotecas e developer tooling. Consulte as vagas Rust e observe como responsabilidades de build, CI, supply chain e performance se conectam ao código de produto.
Perguntas frequentes
Para que serve o cargo tree?
Ele imprime a resolução de dependências do Cargo em formato hierárquico. Isso inclui transitivas que não aparecem no manifesto do pacote e permite analisar versões, features e caminhos de dependência.
Como descobrir quem trouxe uma crate?
Use:
cargo tree -i nome-da-crate
Se houver mais de uma versão, consulte nome-da-crate@versão para separar os caminhos.
Como achar dependências duplicadas?
Execute cargo tree -d. Depois investigue cada versão com o modo invertido. A meta não é zerar o relatório a qualquer custo, mas entender se existe atualização ou ajuste de feature que converja o grafo com segurança.
Como ver features ativadas?
Use cargo tree -e features. Em workspaces, restrinja com -p para entender o pacote relevante e compare as configurações realmente suportadas.
cargo tree altera arquivos?
Não. Ele inspeciona o grafo resolvido. Mudanças acontecem quando você edita Cargo.toml, muda features ou atualiza o Cargo.lock.
Conclusão
cargo tree é o mapa do ecossistema que seu projeto realmente compila. Ele revela dependências transitivas, explica quem trouxe uma crate, mostra versões duplicadas e ajuda a rastrear features que expandiram o grafo.
Comece com três comandos:
cargo tree
cargo tree -i nome-da-crate
cargo tree -d
Depois restrinja por pacote, feature e target conforme a pergunta. Combine o resultado com cargo-outdated, auditorias, testes e medições de tamanho. Assim, decisões sobre dependências deixam de ser baseadas em suposição e passam a ter um caminho causal verificável no grafo do Cargo.