cargo-outdated: Atualize Dependências Rust | Rust Brasil

Use cargo-outdated para encontrar dependências Rust desatualizadas. Guia com semver, workspaces, CI, atualização segura, testes e armadilhas na prática.

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

EtapaComando ou açãoObjetivo
Instalarcargo install cargo-outdated --lockedAdicionar o subcomando localmente
Inspecionarcargo outdatedVer versões atuais, compatíveis e mais recentes
Ver workspacecargo outdated --workspaceAnalisar todos os pacotes do workspace
Atualizar uma cratecargo update -p nome-da-crateAlterar o lockfile de forma focada
Mudar requisitoeditar Cargo.tomlAdotar uma versão fora do intervalo atual
Validar resoluçãocargo tree -dIdentificar versões duplicadas no grafo
Validar códigocargo test --workspace --all-featuresDetectar regressões funcionais
Auditar segurançacargo auditProcurar 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:

  1. “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.
  2. “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:

  • serde possui uma atualização dentro do requisito atual;
  • rand está no máximo compatível com o manifesto, mas existe uma linha mais nova que exige migração;
  • build-lib possui tanto uma atualização compatível quanto uma versão mais recente fora do intervalo;
  • Kind ajuda 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:

FerramentaFunção principalModifica arquivos?
cargo outdatedRelatar versões desatualizadasNormalmente não
cargo updateRecalcular versões no Cargo.lockSim
edição do Cargo.tomlAlterar requisitos declaradosSim
cargo auditComparar o lockfile com advisories RustSecNão corrige automaticamente
cargo treeInspecionar o grafo resolvidoNã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:

  1. vulnerabilidades conhecidas com correção disponível;
  2. bugs que afetam o projeto;
  3. atualizações necessárias para compatibilidade de toolchain ou plataforma;
  4. versões compatíveis de baixo risco;
  5. major versions com migração de API;
  6. 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:

  1. a dependência está centralizada em [workspace.dependencies]?
  2. vários pacotes declaram requisitos divergentes?
  3. alguma feature é ativada apenas por um membro?
  4. a mudança eleva a MSRV do workspace inteiro?
  5. 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 audit em todo PR ou diariamente para advisories conhecidos;
  • cargo outdated semanalmente 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 Compat e Latest de 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 outdated no 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.toml e Cargo.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 audit apó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.