Use cargo-outdated para descobrir quais dependências de um projeto Rust ficaram para trás e separar atualizações compatíveis das que exigem mudança no Cargo.toml. O fluxo básico é instalar a ferramenta, executar o relatório na raiz do repositório e atualizar poucas crates por vez, sempre com testes e revisão do Cargo.lock.
cargo install cargo-outdated --locked
cargo outdated
A ferramenta não atualiza o projeto automaticamente. Ela responde o que está desatualizado; você decide o que atualizar agora, usa cargo update ou edita o manifesto e valida o resultado. Essa separação é saudável: a versão mais nova nem sempre é a melhor mudança para entrar no mesmo pull request.
Resposta rápida: fluxo recomendado
| Etapa | Comando ou ação | Objetivo |
|---|---|---|
| Instalar | cargo install cargo-outdated --locked | Adicionar o subcomando localmente |
| Inspecionar | cargo outdated | Ver versões atuais, compatíveis e mais recentes |
| Ver workspace | cargo outdated --workspace | Analisar todos os pacotes do workspace |
| Atualizar uma crate | cargo update -p nome-da-crate | Alterar o lockfile de forma focada |
| Mudar requisito | editar Cargo.toml | Adotar uma versão fora do intervalo atual |
| Validar resolução | cargo tree -d | Identificar versões duplicadas no grafo |
| Validar código | cargo test --workspace --all-features | Detectar regressões funcionais |
| Auditar segurança | cargo audit | Procurar advisories conhecidos no lockfile |
Antes de automatizar flags em uma esteira crítica, confira cargo outdated --help na versão instalada. Opções e formatos de saída podem evoluir; fixe uma versão homologada da ferramenta quando o pipeline depender do comportamento exato.
O que é cargo-outdated
cargo-outdated é um subcomando do Cargo para comparar as versões usadas pelo projeto com versões mais recentes publicadas no registro. O relatório típico distingue três ideias:
- Project: versão atualmente resolvida no projeto;
- Compat: versão mais nova compatível com o requisito declarado;
- Latest: versão mais recente disponível, mesmo que exija alterar o requisito.
Imagine este manifesto:
[dependencies]
serde = "1.0"
minha-lib = "2.3"
O Cargo.toml não registra necessariamente a versão exata usada no build. Ele declara requisitos. O Cargo.lock guarda a resolução concreta, por exemplo serde 1.0.x e minha-lib 2.3.y.
Se existir uma versão compatível mais nova, o projeto pode adotá-la apenas atualizando o lockfile. Se a versão mais recente estiver fora do requisito — por exemplo, minha-lib 3.0 — será preciso alterar o manifesto e tratar a mudança como uma possível migração de API.
Essa leitura evita duas conclusões erradas:
- “Latest é maior, então devo atualizar imediatamente.” Talvez a versão nova exija Rust mais recente, mude comportamento ou não entregue valor ao projeto.
- “Cargo.toml diz 1.0, então estou usando 1.0.0.” O build usa a versão resolvida no lockfile, não apenas o texto do requisito.
Para revisar o funcionamento do manifesto, lockfile e resolução, veja gerenciamento de dependências em Rust e o guia do Cargo.
Instalando a ferramenta
Instale com o próprio Cargo:
cargo install cargo-outdated --locked
cargo outdated --version
cargo outdated --help
O --locked na instalação pede que o Cargo respeite o lockfile publicado para a ferramenta quando disponível. Em CI, além disso, considere fixar a versão instalada:
cargo install cargo-outdated --version 0.x.y --locked
Substitua 0.x.y pela versão homologada pelo time. Instalar “a mais nova” em toda execução torna a esteira sensível a uma atualização da própria ferramenta, mesmo quando o código do projeto não mudou.
Depois, na raiz do projeto:
cargo outdated
Em um workspace:
cargo outdated --workspace
O primeiro relatório pode ser grande. Não transforme cinquenta linhas em um único pull request mecânico. Classifique antes por risco, impacto e tipo de dependência.
Como interpretar o relatório
Considere uma saída conceitual:
Name Project Compat Latest Kind
serde 1.0.210 1.0.219 1.0.219 Normal
rand 0.8.5 0.8.5 0.9.2 Dev
build-lib 0.3.1 0.3.4 0.4.0 Build
A leitura seria:
serdepossui uma atualização dentro do requisito atual;randestá no máximo compatível com o manifesto, mas existe uma linha mais nova que exige migração;build-libpossui tanto uma atualização compatível quanto uma versão mais recente fora do intervalo;Kindajuda a distinguir dependências normais, de desenvolvimento e de build.
Essa distinção muda a prioridade. Uma crate usada em parsing de entrada não confiável pode merecer revisão antes de uma biblioteca usada apenas por benchmarks. Uma build dependency ainda merece atenção, pois executa durante a compilação, mas seu risco operacional é diferente de uma dependência presente no serviço em runtime.
Compatível não significa livre de risco
Semver reduz incerteza, mas não elimina regressões. Uma atualização compatível pode:
- corrigir um comportamento do qual o projeto dependia sem perceber;
- elevar a MSRV por política do mantenedor;
- ativar código diferente em uma combinação de features;
- mudar performance, tamanho do binário ou tempo de compilação;
- atualizar dependências transitivas;
- introduzir deprecações que ainda compilam.
Por isso, trate Compat como “permitido pelo requisito”, não como “garantido sem impacto”.
cargo-outdated versus cargo update
As ferramentas têm papéis diferentes:
| Ferramenta | Função principal | Modifica arquivos? |
|---|---|---|
cargo outdated | Relatar versões desatualizadas | Normalmente não |
cargo update | Recalcular versões no Cargo.lock | Sim |
edição do Cargo.toml | Alterar requisitos declarados | Sim |
cargo audit | Comparar o lockfile com advisories RustSec | Não corrige automaticamente |
cargo tree | Inspecionar o grafo resolvido | Não |
Para atualizar uma única dependência dentro dos requisitos existentes:
cargo update -p serde
Depois revise o diff:
git diff -- Cargo.lock
cargo test --workspace --all-features
Se a versão desejada exige mudar o manifesto:
[dependencies]
rand = "0.9"
Então regenere e valide:
cargo update -p rand
cargo check --workspace --all-targets --all-features
cargo test --workspace --all-features
cargo clippy --workspace --all-targets --all-features -- -D warnings
Não use cargo update sem olhar o diff. Uma atualização aparentemente focada pode mover várias crates transitivas porque o resolvedor encontrou uma nova combinação válida.
Estratégia segura para atualizar dependências
1. Comece com o repositório limpo
git status --short
cargo outdated
Um working tree limpo facilita atribuir cada mudança à atualização. Também torna o rollback trivial.
2. Classifique as dependências
Uma ordem prática:
- vulnerabilidades conhecidas com correção disponível;
- bugs que afetam o projeto;
- atualizações necessárias para compatibilidade de toolchain ou plataforma;
- versões compatíveis de baixo risco;
- major versions com migração de API;
- atualizações sem benefício claro.
“Está desatualizada” é um sinal de manutenção, não uma gravidade. Para vulnerabilidades conhecidas, use cargo-audit e RustSec, que respondem a uma pergunta diferente.
3. Leia changelog e notas de migração
Antes de uma major version, procure:
- APIs removidas ou renomeadas;
- mudança de features padrão;
- MSRV declarada;
- alteração de runtime, TLS ou backend;
- migrações de tipos e tratamento de erros;
- mudança de licença ou manutenção;
- advisories e correções relevantes.
A versão mais nova pode ser tecnicamente adotável e ainda não caber no ciclo atual do produto.
4. Atualize em lotes pequenos
Prefira um agrupamento coerente:
- runtime async e integrações relacionadas;
- stack HTTP;
- serialização;
- banco de dados;
- tooling de teste;
- build dependencies.
Evite misturar uma migração de framework web, uma troca de TLS e vinte atualizações de dev-dependencies no mesmo commit. Se algo falhar, a investigação fica cara.
5. Revise o grafo
cargo tree
cargo tree -d
cargo tree -d mostra crates com múltiplas versões resolvidas. Duplicidade nem sempre é erro: requisitos incompatíveis podem exigir duas linhas. Ainda assim, ela pode aumentar tempo de compilação, tamanho do artefato e superfície de manutenção.
Para descobrir quem introduziu uma dependência:
cargo tree -i nome-da-crate
Isso é especialmente útil quando o cargo-outdated aponta uma dependência transitiva que não aparece no seu Cargo.toml.
6. Valide mais do que compilação
Uma atualização está pronta quando passa pelos controles relevantes do projeto:
cargo fmt --check
cargo check --workspace --all-targets --all-features
cargo test --workspace --all-features
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo doc --workspace --no-deps
Nem todo projeto consegue habilitar todas as features ao mesmo tempo. Crates com backends mutuamente exclusivos precisam de uma matriz explícita. Nesse caso, cargo-hack ajuda a testar combinações reais em vez de confiar apenas em --all-features.
Se a atualização toca código crítico de performance, rode benchmarks comparáveis. Se muda serialização, banco ou protocolo, inclua testes de compatibilidade e migração.
Aplicações e bibliotecas exigem políticas diferentes
Aplicações e serviços
Aplicações normalmente versionam o Cargo.lock. O objetivo é reproduzir o mesmo grafo em desenvolvimento, CI e produção:
cargo build --locked
cargo test --locked
A rotina de atualização modifica o lockfile de forma intencional, valida o produto e entrega aquela resolução. O risco principal é regressão no comportamento da aplicação.
Bibliotecas publicadas
Uma biblioteca declara intervalos para consumidores que resolverão seus próprios grafos. Testar apenas o lockfile do mantenedor não cobre todas as versões permitidas pelo manifesto.
Além do fluxo normal, bibliotecas devem considerar:
- MSRV prometida;
- menor conjunto de versões suportadas, quando aplicável à política do projeto;
- features opcionais isoladas;
- compatibilidade semver da API pública;
- documentação e exemplos;
- impacto sobre consumidores.
Use cargo-semver-checks para detectar quebras na API pública e cargo-msrv para manter a versão mínima declarada sob controle.
Workspaces: como evitar um relatório caótico
Em um workspace grande, execute:
cargo outdated --workspace
Depois agrupe por responsabilidade. Um workspace pode conter:
- biblioteca pública;
- API;
- worker;
- CLI administrativa;
- ferramentas internas;
- benchmarks e fixtures.
Uma atualização do runtime pode afetar quase todos os pacotes; uma dev-dependency pode existir em apenas um. Faça perguntas concretas:
- a dependência está centralizada em
[workspace.dependencies]? - vários pacotes declaram requisitos divergentes?
- alguma feature é ativada apenas por um membro?
- a mudança eleva a MSRV do workspace inteiro?
- exemplos e benches também compilam?
O guia de Cargo workspaces e monorepos mostra como centralizar políticas sem esconder necessidades específicas de cada pacote.
Usando cargo-outdated na CI
Falhar todo pull request apenas porque uma crate publicou versão nova costuma gerar ruído. A publicação pode acontecer minutos depois de o desenvolvedor abrir o PR, sem qualquer relação com a mudança revisada.
Uma política mais útil é executar cargo-outdated periodicamente:
name: dependencias-desatualizadas
on:
workflow_dispatch:
schedule:
- cron: "0 9 * * 1"
jobs:
outdated:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Instalar cargo-outdated
run: cargo install cargo-outdated --locked
- name: Gerar relatório
run: cargo outdated --workspace
Esse exemplo gera sinal, mas a operação real ainda precisa decidir como transformar o resultado em issue, log ou pull request. Se você configurar a ferramenta para retornar código de saída não zero diante de versões antigas, confirme a flag suportada pela versão fixada com cargo outdated --help.
Uma cadência razoável pode combinar:
cargo auditem todo PR ou diariamente para advisories conhecidos;cargo outdatedsemanalmente para manutenção planejada;- atualização automática de baixo risco por bot, com testes obrigatórios;
- revisão manual para major versions e componentes críticos.
A distinção importa: desatualizado não é sinônimo de vulnerável, e sem advisory não é sinônimo de seguro.
Dependabot, Renovate e cargo-outdated
Bots criam pull requests; cargo-outdated oferece uma visão local e sob demanda. Eles são complementares.
Use um bot quando você quer:
- pull requests pequenos e frequentes;
- agrupamento por ecossistema;
- regras de agenda;
- visibilidade contínua no repositório.
Use cargo-outdated quando você quer:
- diagnosticar o estado antes de planejar uma sprint;
- inspecionar um workspace localmente;
- comparar
CompateLatestde uma vez; - investigar por que o bot não propôs determinada versão;
- validar uma política sem depender de uma plataforma específica.
Mesmo com automação, mantenha limites de PRs abertos, agrupamentos coerentes e testes. Cinquenta atualizações automáticas simultâneas não são manutenção contínua; são uma fila que ninguém consegue revisar.
Supply chain: atualização é só uma parte
Manter dependências recentes reduz tempo acumulado de migração e pode acelerar correções, mas uma política de supply chain também precisa considerar:
- advisories conhecidos;
- origem da crate e do repositório;
- mantenedores e atividade recente;
- licenças;
- features habilitadas;
- dependências transitivas;
- scripts de build e proc-macros;
- revisão de mudanças sensíveis;
- inventário ou SBOM quando necessário.
O artigo sobre cargo-vet e auditoria de dependências mostra uma abordagem baseada em confiança e revisões. O guia de supply chain com cargo-deny e SBOM amplia a análise para licenças, fontes e inventário.
Atualizar cegamente para “latest” não substitui nenhuma dessas práticas.
Armadilhas comuns
Atualizar tudo no mesmo pull request
O build quebra e ninguém sabe qual crate causou o problema. Faça lotes pequenos e commits reversíveis.
Confundir versão compatível com mudança sem risco
Semver é um contrato útil, não uma prova. Rode os testes adequados e observe MSRV, features e comportamento.
Ignorar o Cargo.lock
Em aplicações, é o lockfile que registra o grafo entregue. Revise seu diff como código.
Olhar apenas dependências diretas
O risco e o peso do projeto também vivem nas transitivas. Use cargo tree -i para encontrar a origem.
Forçar latest sem benefício
Uma major version pode exigir migração, elevar a MSRV e aumentar o binário sem resolver qualquer problema atual. Atualize com uma razão documentada.
Deixar exceções sem prazo
Se uma crate não pode ser atualizada por incompatibilidade, registre o bloqueio, a versão alvo, o responsável e a condição de saída. “Depois vemos” vira dívida permanente.
Fazer a CI falhar por ruído
Um relatório periódico é frequentemente mais útil do que bloquear todo PR por uma publicação externa recém-lançada.
Checklist de atualização
- executar
cargo outdatedno repositório limpo; - separar atualizações compatíveis de major versions;
- priorizar vulnerabilidades, bugs e necessidades reais;
- ler changelog, guia de migração e política de MSRV;
- atualizar uma crate ou grupo coerente por vez;
- revisar o diff de
Cargo.tomleCargo.lock; - inspecionar transitivas com
cargo tree -i; - verificar duplicidades com
cargo tree -d; - rodar fmt, check, testes, Clippy e documentação;
- testar combinações de features relevantes;
- executar benchmarks quando performance for requisito;
- rodar
cargo auditapós alterar o grafo; - documentar exceções temporárias com prazo;
- manter commits pequenos e fáceis de reverter.
cargo-outdated no portfólio e na carreira Rust
Saber adicionar crates é básico. Saber mantê-las demonstra experiência operacional. Em um projeto de portfólio, mostre:
- política de atualização no
CONTRIBUTING.md; - job periódico de dependências desatualizadas;
- auditoria RustSec;
- testes de features no workspace;
- MSRV declarada para bibliotecas;
- pull requests pequenos com changelog e decisão de risco;
- métricas ou benchmarks para componentes sensíveis.
Esse tipo de prática aparece em equipes de backend, infraestrutura, ferramentas de desenvolvedor, segurança e sistemas. Consulte as vagas Rust para entender quais stacks e responsabilidades aparecem nas oportunidades atuais.
Perguntas frequentes
Para que serve cargo-outdated?
Ele mostra dependências resolvidas que possuem versões mais novas e diferencia, no relatório, atualizações compatíveis com o requisito atual das versões mais recentes que podem exigir alteração do manifesto.
Qual a diferença para cargo update?
cargo-outdated informa. cargo update recalcula e grava versões no Cargo.lock dentro dos requisitos declarados. Para adotar uma versão fora do intervalo, altere também o Cargo.toml.
Ele modifica o Cargo.toml?
Não é essa a função da ferramenta. Mudanças de requisito devem ser conscientes, revisadas e acompanhadas de validação.
Como usar em workspace?
Comece com cargo outdated --workspace, agrupe resultados por pacote e responsabilidade e atualize em lotes pequenos. Teste todos os membros, targets e combinações de features relevantes.
Devo bloquear a CI se existir qualquer versão nova?
Geralmente não. Use o relatório em uma rotina periódica e bloqueie por critérios de segurança ou compatibilidade claramente definidos. Versão nova, sozinha, não determina urgência.
Conclusão
cargo-outdated transforma a sensação vaga de “as dependências estão antigas” em um relatório acionável. Ele mostra o que o projeto usa, o que cabe no requisito atual e o que exigiria uma migração maior.
O melhor fluxo não é atualizar tudo. É inspecionar, classificar, ler as mudanças, atualizar em lotes pequenos e validar o produto real. Combine cargo-outdated com cargo update, cargo tree, testes de features, cargo-audit e controles de compatibilidade.
Comece hoje com cargo outdated, escolha uma dependência compatível de baixo risco e faça uma atualização focada. Revise o Cargo.lock, rode a suíte completa e registre o motivo da mudança. Uma rotina assim mantém projetos Rust atuais sem transformar cada atualização em uma migração imprevisível.