Clippy e rustfmt
LidoNesta aula, você aprenderá a usar o Clippy e o rustfmt, ferramentas essenciais para escrever código Rust limpo e consistente. Exploraremos lints úteis, configuração, formatação automática e integração com CI, com exemplos práticos e exercícios.
Bem-vindo à aula sobre Clippy e rustfmt, duas ferramentas indispensáveis no ecossistema Rust. Elas ajudam a manter seu código limpo, consistente e livre de erros comuns. Enquanto o rustfmt cuida da formatação (espaços, quebras de linha, indentação), o Clippy analisa seu código em busca de padrões problemáticos, oferecendo sugestões de melhoria. Nesta aula, você aprenderá a configurá-los, usá-los no dia a dia e integrá-los ao seu fluxo de trabalho, incluindo pipelines de CI.
Essas ferramentas são parte do toolchain padrão do Rust e podem ser instaladas com o rustup. Elas são amplamente adotadas pela comunidade e frequentemente usadas em projetos open source, então dominá-las é essencial para colaborar em projetos reais.
Lints úteis
Clippy é o linter oficial do Rust. Ele contém uma coleção de lints que vão além do compilador, detectando estilos de código que podem ser simplificados, propensos a erros ou menos eficientes. Você pode executá-lo com cargo clippy, e ele analisará todos os alvos do seu projeto.
Alguns lints são extremamente úteis no dia a dia. Por exemplo, clippy::single_match sugere usar if let em vez de match quando há apenas um braço relevante. clippy::needless_return aponta quando o return é desnecessário, e clippy::collapsible_if sugere combinar ifs aninhados. Vamos ver um exemplo:
fn main() {
let x = 5;
match x {
0 => println!("zero"),
_ => println!("não-zero"),
}
}Clippy sugerirá usar if let para o caso 0, pois o outro braço é capturado por _. Isso torna o código mais conciso e legível.
Outro lint útil é clippy::map_clone, que sugere usar cloned() em iteradores quando você está mapeando apenas para clonar. Além disso, clippy::needless_range_loop recomenda usar iteradores em vez de índices quando possível. Esses lints ajudam a escrever código mais idiomático e eficiente.
Você pode controlar a severidade dos lints no lib.rs ou main.rs com atributos, ou no arquivo Cargo.toml usando a seção [lints.clippy]. Por exemplo, para permitir um lint específico em todo o crate, você pode usar:
#![allow(clippy::needless_return)]Ou, para negar (tornar erro) um lint, use #![deny(clippy::unwrap_used)], o que é útil em projetos críticos.
Formatação automática
O rustfmt é o formatador oficial do Rust. Ele garante que todo o código siga um estilo consistente, eliminando discussões sobre formatação em code reviews. Para formatar um projeto, basta executar cargo fmt, que formata todos os arquivos .rs no diretório atual e subdiretórios.
O estilo padrão do rustfmt é baseado nas diretrizes oficiais da comunidade. Por exemplo, ele usa 4 espaços para indentação, chaves na mesma linha para blocos, e espaços ao redor de operadores. Aqui está um exemplo de código não formatado:
fn add(a:i32,b:i32)->i32{a+b}Após rodar cargo fmt, o código se torna:
fn add(a: i32, b: i32) -> i32 {
a + b
}Isso torna a leitura muito mais fácil. O rustfmt também pode ser usado como uma biblioteca, mas a forma mais comum é via CLI. Ele pode ser configurado através de um arquivo rustfmt.toml ou .rustfmt.toml no diretório raiz do projeto.
Algumas opções de configuração incluem max_width (largura máxima da linha, padrão 100), tab_spaces (número de espaços por tab), e use_field_init_shorthand (se deve usar a sintaxe abreviada de inicialização de campos). É importante notar que o rustfmt não faz alterações que mudem o comportamento do código, apenas a formatação.
Integração CI
Integrar Clippy e rustfmt em seu pipeline de Integração Contínua (CI) é uma prática recomendada. Isso garante que todo o código que entra no repositório esteja formatado e livre de lints problemáticos. Você pode adicionar etapas no seu CI (como GitHub Actions, GitLab CI, etc.) para executar cargo fmt --check e cargo clippy -- -D warnings.
O comando cargo fmt --check verifica se o código está formatado sem modificar nada, retornando um erro se houver diferenças. Já cargo clippy -- -D warnings trata todos os avisos do Clippy como erros, fazendo o build falhar se houver qualquer lint.
Um exemplo de workflow no GitHub Actions seria:
name: CI
on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- run: cargo fmt --check
- run: cargo clippy -- -D warnings
Essa configuração garante que qualquer PR que não esteja formatado ou contenha lints falhe no CI, mantendo a base de código limpa.
Além disso, você pode configurar o Clippy para negar certos lints no CI, usando a seção [lints] no Cargo.toml. Isso centraliza as políticas de lint do projeto.
Configuração
Tanto o Clippy quanto o rustfmt podem ser configurados de forma granular. Para o rustfmt, você pode criar um arquivo rustfmt.toml na raiz do projeto. Por exemplo, para definir uma largura máxima de 80 caracteres e usar 2 espaços de indentação:
max_width = 80
tab_spaces = 2
Já o Clippy é configurado principalmente através de atributos no código ou na seção [lints.clippy] do Cargo.toml. Por exemplo, para permitir o lint clippy::too_many_arguments em todo o crate:
[lints.clippy]
too_many_arguments = "allow"
Você também pode configurar níveis de severidade: allow, warn, deny e forbid. É comum usar deny para lints que você considera críticos.
Além disso, você pode desabilitar completamente o Clippy ou o rustfmt em um trecho de código usando #[allow(clippy::some_lint)] ou #[rustfmt::skip] para pular a formatação de um item específico. Isso é útil em casos excepcionais, como código gerado por macros.
Uma boa prática é configurar seu editor para rodar o rustfmt automaticamente ao salvar e o Clippy em segundo plano. Isso evita acumular problemas e facilita a adesão aos padrões do projeto.
Boas práticas e observações finais
Essas ferramentas são projetadas para serem usadas em conjunto. Enquanto o rustfmt cuida da forma, o Clippy cuida do conteúdo. Aqui estão algumas boas práticas:
- Execute
cargo fmtecargo clippyregularmente, de preferência antes de cada commit. - Use
cargo fmt --checkno CI para garantir que o código está formatado. - Trate os avisos do Clippy como erros no CI usando
-D warnings. - Configure o Clippy com lints adicionais, como
clippy::pedanticeclippy::nursery, para análises mais rigorosas. - Documente as configurações do projeto em um arquivo
rustfmt.tomle na seção[lints]do Cargo.toml para que todos os contribuidores sigam o mesmo estilo.
Lembre-se de que essas ferramentas são opiniáticas, mas a consistência que elas trazem é valiosa para projetos de qualquer tamanho.
Referências
- Documentação oficial do Clippy
- Documentação oficial do rustfmt
- cargo clippy - Comando do Cargo
- cargo fmt - Comando do Cargo
- Configuração do rustfmt
- Configuração do Clippy
- Repositório do Clippy no GitHub
- Repositório do rustfmt no GitHub
Exercícios
- Exercício 1: Crie um projeto Rust simples com um arquivo
main.rscontendo uma função que recebe dois números e retorna o maior. Escreva o código de forma não formatada (ex.: sem espaços, chaves na mesma linha) e depois executecargo fmt. Mostre o antes e o depois.
Exemplo de código não formatado:
fn max(a:i32,b:i32)->i32{if a>b{a}else{b}}Após cargo fmt:
fn max(a: i32, b: i32) -> i32 {
if a > b { a } else { b }
}clippy::single_match. Execute cargo clippy e mostre a sugestão. Corrija o código conforme a sugestão.Código problemático:
fn main() {
let x = 10;
match x {
0 => println!("zero"),
_ => println!("outro"),
}
}Clippy sugere usar if let:
fn main() {
let x = 10;
if let 0 = x {
println!("zero");
} else {
println!("outro");
}
}rustfmt.toml para que o rustfmt use largura máxima de 80 caracteres e indentação de 2 espaços. Crie um exemplo de código que seja reformatado de acordo com essa configuração.Arquivo rustfmt.toml:
max_width = 80
tab_spaces = 2
Exemplo de código antes:
fn main() { let x = 1; let y = 2; println!("{}", x + y); }Depois:
fn main() {
let x = 1;
let y = 2;
println!("{}", x + y);
}cargo fmt --check e cargo clippy -- -D warnings.Exemplo de workflow:
name: CI
on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- run: cargo fmt --check
- run: cargo clippy -- -D warnings
clippy::pedantic (por exemplo, clippy::cast_possible_truncation) e explique como você poderia configurá-lo para ser permitido ou negado no projeto.Código com possível truncamento:
fn main() {
let x: i64 = 1000;
let y = x as i32;
println!("{}", y);
}Para permitir esse lint no projeto, adicione no Cargo.toml:
[lints.clippy]
cast_possible_truncation = "allow"
Ou negar com "deny".