Rust QC
Este skill estabelece um padrão para código Rust e o aplica em três modos. As regras vêm do Rust Style Guide, do Rust API Guidelines, do Microsoft Pragmatic Rust Guidelines (2026.6), do Google Rust Style Guide e das práticas visíveis em memchr, jiff, ripgrep, fd e dua-cli. Onde essas fontes discordam, references/precedence.md registra a decisão para que você nunca precise reabrir a discussão.
Passo 1: Estabelecer contexto (todos os modos)
Faça isso antes de escrever ou julgar qualquer código. Pare e relate se uma pré-condição falhar; não adivinhe sobre um toolchain ou manifest ausente.
- Encontre a raiz do crate (o
Cargo.tomlmais próximo). Observe se é um workspace, uma biblioteca, um binário, ou ambos, e se é publicado (publish = falseou consumido apenas dentro do workspace ou através de FFI significa interno). - Leia as regras próprias do projeto primeiro, porque elas substituem os padrões deste skill:
rustfmt.tomlou.rustfmt.toml,clippy.toml,[lints]e[workspace.lints]emCargo.toml,edition,rust-version(MSRV),rust-toolchain.toml,CONTRIBUTING.md,AGENTS.mdouCLAUDE.md, e qualquerAI_POLICY.md. SeAI_POLICY.mdouCONTRIBUTING.mdrestringir contribuições de IA ou agentes, avise o usuário antes de produzir alterações destinadas a esse repositório. - Classifique o código como biblioteca ou aplicação. As regras de design de API são obrigatórias para bibliotecas e recomendações fortes para aplicações, que devem favorecer a simplicidade em vez da generalidade.
- Observe as plataformas. Se
rust-toolchain.tomllistar alvos ou o código for pesado com portões no estilo#[cfg(windows)]para uma plataforma diferente deste host, execute a verificação com--target <triple>também (lint e documenta para esse alvo; testes são ignorados). Avisos como código morto que aparecem apenas no host por causa de portões de plataforma são artefatos do host, não achados. - Execute
scripts/check.sh <crate-root>uma vez para registrar a linha de base. Você precisa dela para distinguir suas próprias regressões de dívidas pré-existentes. Use--features "<list>"em vez do padrão--all-featuresquando o crate tiver features mutuamente exclusivas.
Não instale toolchains, componentes ou ferramentas (nightly, Miri, cargo-hack) e não use a rede além de buscar crates a menos que o usuário peça. Quando uma verificação precisar de algo ausente, diga e recomende.
Passo 2: Escolher o modo
Criar quando o usuário quiser novas funções, métodos, tipos, módulos ou crates. Siga "Criando código" abaixo.
Modificar quando o usuário quiser alterar código existente: uma funcionalidade, refinamento, correção de bug, refatoração ou remediação de achados de auditoria. Siga "Modificando código" abaixo.
Auditar quando o usuário quiser código existente revisado, avaliado ou verificado, com problemas identificados e correções propostas. Siga "Auditando código" abaixo. Auditoria não altera código a menos que o usuário peça; ela produz achados.
Uma solicitação pode abranger modos ("audite este módulo e corrija o que encontrar"). Execute a auditoria primeiro, relate, depois mude para modificar para as correções.
Regras inegociáveis
Estas se aplicam em todos os modos. O conjunto completo de regras, com IDs, vive nos arquivos de referência listados no final.
- Formatação é trabalho do rustfmt. Nunca formate manualmente contra o rustfmt. Nunca adicione
#[rustfmt::skip]exceto para alinhamento deliberado (tabelas, matrizes) ou código gerado. - Código é seguro. Código seguro não deve ser capaz de causar comportamento indefinido. Cada bloco
unsafetem um comentário// SAFETY:diretamente acima dele explicando por que cada pré-condição é válida. Cadaunsafe fneunsafe traittem uma seção de documentação# Safety. Useunsafeapenas para FFI, uma necessidade de desempenho medida por benchmark, ou uma abstração nova, e nunca para contornarSend,Syncou lifetimes. Vejareferences/unsafe.md. - Erros versus pânicos. Falhas recuperáveis causadas por entrada ou ambiente retornam
Result(ouOptionquando a causa é óbvia). Bugs de programação detectados causam pânico. Código de biblioteca nunca entra em pânico com entrada do chamador que poderia razoavelmente ser inválida. Vejareferences/errors.md. - Erros de biblioteca permitem que os chamadores façam ramificações sem analisar texto. Um tipo de erro concreto com um método
kind()retornando um enum#[non_exhaustive]público, ou predicadosis_*(). Campos privados são corretos; um kind privado sem accessor é um defeito. Mensagens apontam para o token e posição ofensivos, e nunca copiam toda a entrada. - Nenhum
unwrap()puro em código de produção. Use?, correspondência de padrões, ouexpect("...")cuja mensagem afirma por que o invariante é válido.unwrap()é aceitável em testes. Exemplos de documentação usam?com a linha oculta# Ok::<(), Box<dyn std::error::Error>>(()). - Cada nova dependência é um custo, dev-dependencies incluídas. Adicione uma apenas quando ela remove complexidade real ou fornece correção que você não pode escrever razoavelmente (Unicode case folding é uma boa razão; um framework de teste de propriedades para um parser de 100 linhas não é). Diga por que em seu relatório.
- Estrutura corresponde ao tamanho. Não crie um módulo para uma função pequena e seu tipo de erro; um módulo merece seu arquivo com um conceito distinto e vários itens ou algumas centenas de linhas.
- Sobrescritas de lint usam
#[expect(lint, reason = "...")], não#[allow], para que sobrescritas obsoletas apareçam. - Itens públicos são documentados, a primeira frase é uma linha de cerca de 15 palavras, e funções incluem seções
# Errors,# Panicse# Safetysempre que aplicável. Vejareferences/documentation.md. - Tipos públicos implementam
Debug. Tipos destinados a serem lidos por pessoas também implementamDisplay. - Comentários explicam o porquê, não o quê. Se um comentário reafirma o código, exclua-o. O comentário do alocador do ripgrep em
crates/core/main.rsé o modelo: decisão, motivo e as condições sob as quais ele se aplica. - Nenhum diagrama ASCII-art ou caracteres de desenho de caixa em comentários ou documentação. Descreva a estrutura em frases; desenhos são ilegíveis com um leitor de tela e apodrecem quando o código muda. (Convenção da equipe, não upstream.)
- Nenhum conteúdo de diário de design na documentação. A documentação descreve o comportamento atual, não como o código veio a ser ou quais diretrizes foram seguidas.
- Testes verificam comportamento. Nenhum teste que reafirme uma constante ou espelhe os ramos da implementação.
Criando código
Trabalhe de fora para dentro: tipos e assinaturas primeiro, depois documentação, depois implementação, depois testes. Escrever o comentário de documentação antes do corpo é a maneira mais rápida de descobrir uma API confusa.
- Posicione-o. Coloque o item no módulo que possui seu conceito; em um crate pequeno isso é frequentemente
lib.rs. Novos módulos são nomeados após seu tipo principal e só criados quando o código justifica um (ORG-19). Vejareferences/organization.md. - Modele com tipos. Newtypes para valores que são fáceis de misturar ou carregam invariantes, com construtores que impõem o invariante. Um
enumem vez de um parâmetroboolcujo significado não é óbvio no local da chamada. Campos nomeados sobre tuplas. Torne estados inválidos irrepresentáveis. - Modele a assinatura. Pegue emprestado o que você só lê (
&str,&[T],&Path,Option<&T>), tome posse apenas quando você armazena ou consome. Em bibliotecas, aceiteimpl AsRef<Path>ouimpl IntoIteratoronde remove atrito; em aplicações, mantenha tipos concretos. Builders para tipos com várias configurações opcionais, validando embuild(). Vejareferences/api-design.md. - Nomeie-o. Maiúsculas/minúsculas RFC 430; conversões
as_/to_/into_; construtoresnew,with_*,try_new; gettersfoo(). Nenhuma palavra vaga (Manager,Service,Factory,Helper,Util), e nenhum nome que colida com um significado existente na mesma interface (NAME-9). - Documente-o de acordo com
references/documentation.md, incluindo um exemplo executável para itens públicos, e a unidade de qualquer posição ou comprimento que você retorna. - Implemente-o. Funções de propósito único. Um loop
forsimples sobre uma cadeia de combinadores quando a cadeia precisa de condicionais ou estado externo; combinadores quando eles substituem lógica escrita à mão (any,position,sum). Anotações de tipo onde a inferência deixaria um leitor adivinhando. Texto humano recebe tratamento ciente de Unicode: correspondência sem distinção entre maiúsculas e minúsculas usa Unicode case folding, ou documenta suas lacunas (API-27). - Lide com erros de acordo com
references/errors.md: em bibliotecas um tipo de erro concreto com um kind ou predicados públicos; em aplicaçõesanyhowcom.context(). - Teste-o de acordo com
references/testing.md: testes unitários inline, testes de integração sobtests/, e verificações de invariantes amplas que não precisam de novas dependências (enumere entradas curtas, ou um loop com semente contra uma referência ingênua). - Verifique com a verificação abaixo.
Modificando código
O objetivo é a menor alteração correta que deixe o código tocado pelo menos tão claro quanto você o encontrou.
- Leia antes de editar. Leia a função inteira, seus chamadores, seus testes e a API vizinha. Identifique convenções locais (estilo de importação, tipo de erro, estilo de cabeçalho de documentação como
# Exampleversus# Examples, largura de linha, densidade, nomes de parâmetros e chaves) e corresponda a elas, mesmo onde os padrões deste skill diferem. - Correções de bug começam com um teste com falha que cobre a classe do bug. Reproduza a entrada relatada, depois adicione os casos vizinhos que a mesma causa raiz quebra (mais elementos, outras ordenações, combinações). Veja-os falhar, corrija a causa raiz, veja-os passar. Se o projeto mantiver testes de regressão em um lugar (ripgrep's
tests/regression.rs), adicione o seu lá. - Corrija a causa, não o sintoma. Um novo
unwrap_or_default(),clone()ou retorno antecipado que silencia uma falha não é uma correção a menos que você possa explicar por que o caminho original estava errado. Coloque um comentário de uma linha na correção se o motivo não for óbvio a partir do código. - Fique cirúrgico. Não renomeie, reordene ou reformate código que você não foi solicitado a tocar. Liste problemas não relacionados para o usuário em vez de corrigi-los silenciosamente.
- Mantenha APIs e ABIs públicas estáveis a menos que o usuário aprove uma alteração disruptiva. Adicionar um campo a uma estrutura pública com campos públicos, uma variante a um enum público exaustivo, um método de trait obrigatório ou alterar um contrato FFI é disruptivo.
- Atualize a documentação e os testes com o código. Uma assinatura ou comportamento alterado significa
# Errors,# Panics, exemplos e testes alterados na mesma alteração. - Formatação: se a linha de base estava limpa com rustfmt, execute
cargo fmt. Se não estava, formate apenas o que você alterou e avise o usuário. Serustfmt.tomlusar opções exclusivas do nightly (rustfmt estável avisa que são "only available in nightly channel"), o padrão do projeto écargo +nightly fmt; use-o se estiver instalado, e caso contrário não formate nada automaticamente e diga isso, porque o rustfmt estável reescreveria o código contra as configurações do projeto. - Remediação de uma auditoria: corrija achados em ordem de gravidade, um achado por alteração lógica, e nomeie o achado em seu resumo.
- Verifique com a verificação abaixo e confirme que você não introduziu novos avisos em relação à linha de base, em cada alvo que você verificou no passo 1.
Auditando código
-
Execute
scripts/check.sh --audit <crate-root>(mais--targetconforme passo 1). A passagem--auditrelata, com acessibilidade da API pública,unsafenão documentado, documentação ausente e seções# Errors/# Panics,unwrapfora de testes,#[allow]sem motivo,dbg!/todo!, importações glob, e asserções sem mensagens. Relate esses resultados de higiene de forma compacta: um achado Menor por categoria (documentação,Debug, comentários SAFETY, configuração de lint), cada um com uma contagem e as poucas instâncias que mais importam. Uma etapa de verificação que falha apenas porque o toolchain aqui é mais novo do que aquele que o CI do projeto ourust-toolchain.tomlusa é desvio de toolchain: um achado Menor, não um Principal. -
Execute
python3 -I scripts/audit_scan.py <crate-root>para o que o clippy não pode ver: reexportações glob, nomes de tipo com palavras vagas, comentários ASCII-art, documentação de histórico de design, tabelas de parâmetros, TODOs puros, mensagensexpectvazias e macros de impressão em código de biblioteca. Confirme cada acerto lendo o código; descarte falsos positivos. -
Teste o comportamento como os usuários o encontram. É aqui que a maioria dos bugs voltados para o usuário em crates reais é encontrada, então gaste a maior parte da auditoria aqui e no passo 5. Leia a documentação, especificação e testes do crate para listar as funcionalidades de entrada que ele afirma lidar. Para cada uma, construa uma pequena entrada realista que os testes existentes não cobrem, execute-a através da API pública de um crate descartável fora do workspace e compare a saída com o que um usuário deveria obter. Cubra as funcionalidades que o formato permite, mas que as fixtures ignoram:
- valores herdados, indiretos ou padrão
- duplicatas
- partes opcionais ou ocultas
- danos leves que outras ferramentas toleram
- texto não ASCII e codificações legadas
- tamanhos grandes mas legítimos
Relate cada problema confirmado com a entrada que o mostra.
-
Teste contra entradas não confiáveis. Para cada função pública que analisa bytes ou texto não confiáveis, alimente-a com toda entrada curta sobre um alfabeto relevante ou alguns milhões de mutações aleatórias com semente, e capture pânicos. Também tente estruturas artificiosas que fazem o trabalho crescer mais rápido que o tamanho da entrada (referências compartilhadas ou recursivas, aninhamento profundo) sob um tempo limite.
-
Leia o código para o que ferramentas não podem julgar: solidez de cada bloco
unsafe, design de erro, formato da API, limites de módulo, nomenclatura, qualidade do teste e se comentários explicam o porquê. Em código que fala com o sistema operacional, também verifique:- pânicos em uma chamada de sistema, I/O ou falha de ambiente (
assert!ouunwrapem um valor de retorno, uma variável de ambiente ou campo passwd não UTF-8) - código entre
forkeexec(closurespre_exec) ou em um manipulador de sinal que aloca, trava, formata, registra ou sai, nenhum dos quais é seguro para sinal assíncrono - descritores de arquivo ou handles vazados, e sinais enviados a um ID de processo que pode ter sido coletado e reutilizado
- conversões de estreitamento e aritmética não verificada em tamanhos, contagens e coordenadas
Use
references/audit-checklist.md. - pânicos em uma chamada de sistema, I/O ou falha de ambiente (
-
Relate usando o formato abaixo. Proponha uma correção concreta para cada achado.
Os passos 1 e 2 são um piso rápido, não a auditoria: gaste não mais do que cerca de um quinto do tempo neles. Uma auditoria que lista principalmente melhorias de lint, documentação e s...
[truncado para tradução]