Clippy: Lints Mais Comuns e Como Resolver
O Clippy é a ferramenta oficial de linting do Rust, com mais de 700 lints que ajudam a escrever código mais idiomático, performático e correto. Este guia cobre os lints mais frequentes que desenvolvedores encontram no dia a dia.
Como Usar o Clippy
# Executar clippy
cargo clippy
# Corrigir automaticamente (quando possível)
cargo clippy --fix
# Tratar warnings como erros (útil em CI)
cargo clippy -- -D warnings
# Todos os lints, incluindo pedantic
cargo clippy -- -W clippy::pedantic
Os Lints Mais Comuns
1. clippy::needless_return — Return Desnecessário
O Rust é uma linguagem baseada em expressões. A última expressão de uma função é seu valor de retorno — return explícito é desnecessário.
// AVISO: needless return
fn somar(a: i32, b: i32) -> i32 {
return a + b;
}
// CORRETO: expressão final sem return nem ;
fn somar(a: i32, b: i32) -> i32 {
a + b
}
Com condicionais:
// AVISO
fn classificar(n: i32) -> &'static str {
if n > 0 {
return "positivo";
} else {
return "não-positivo";
}
}
// CORRETO
fn classificar(n: i32) -> &'static str {
if n > 0 {
"positivo"
} else {
"não-positivo"
}
}
2. clippy::redundant_clone — Clone Desnecessário
Clone é usado quando não é necessário, pois o valor não será mais usado depois.
// AVISO: clone redundante
fn processar(nome: String) {
let copia = nome.clone();
println!("{}", copia);
// `nome` nunca mais é usado — clone era desnecessário
}
// CORRETO: usar o valor diretamente
fn processar(nome: String) {
println!("{}", nome);
}
3. clippy::unused_imports — Imports Não Utilizados
// AVISO: imports não utilizados
use std::collections::HashMap;
use std::io::Read;
fn main() {
// Nenhum dos imports é usado
println!("Olá");
}
// CORRETO: remova os imports não utilizados
fn main() {
println!("Olá");
}
4. clippy::manual_map — Map Manual em Option
// AVISO: manual implementation of `Option::map`
fn dobrar(valor: Option<i32>) -> Option<i32> {
match valor {
Some(v) => Some(v * 2),
None => None,
}
}
// CORRETO: use .map()
fn dobrar(valor: Option<i32>) -> Option<i32> {
valor.map(|v| v * 2)
}
5. clippy::single_match — Match com Apenas Um Padrão
// AVISO: use if let em vez de match com um padrão
fn verificar(valor: Option<i32>) {
match valor {
Some(v) => println!("{}", v),
_ => {},
}
}
// CORRETO: if let
fn verificar(valor: Option<i32>) {
if let Some(v) = valor {
println!("{}", v);
}
}
6. clippy::iter_cloned_collect — Iterador com Clone Desnecessário
// AVISO
fn copiar_lista(lista: &[String]) -> Vec<String> {
lista.iter().map(|s| s.clone()).collect()
}
// CORRETO: use .cloned() ou .to_vec()
fn copiar_lista(lista: &[String]) -> Vec<String> {
lista.to_vec()
}
7. clippy::len_zero — Comparação com len() == 0
// AVISO: use is_empty() em vez de len() == 0
fn vazio(lista: &Vec<i32>) -> bool {
lista.len() == 0
}
// CORRETO
fn vazio(lista: &Vec<i32>) -> bool {
lista.is_empty()
}
8. clippy::needless_borrow — Empréstimo Desnecessário
// AVISO: empréstimo desnecessário
fn imprimir(texto: &String) {
println!("{}", &texto); // & é desnecessário
}
// CORRETO
fn imprimir(texto: &String) {
println!("{}", texto);
}
// MELHOR AINDA: use &str em vez de &String
fn imprimir(texto: &str) {
println!("{}", texto);
}
9. clippy::collapsible_if — If Aninhados que Podem Ser Combinados
// AVISO
fn verificar(a: bool, b: bool) {
if a {
if b {
println!("ambos verdadeiros");
}
}
}
// CORRETO
fn verificar(a: bool, b: bool) {
if a && b {
println!("ambos verdadeiros");
}
}
10. clippy::match_like_matches_macro — Use matches!()
// AVISO
fn eh_vogal(c: char) -> bool {
match c {
'a' | 'e' | 'i' | 'o' | 'u' => true,
_ => false,
}
}
// CORRETO
fn eh_vogal(c: char) -> bool {
matches!(c, 'a' | 'e' | 'i' | 'o' | 'u')
}
11. clippy::map_flatten — Use flat_map
// AVISO
fn processar(lista: Vec<Vec<i32>>) -> Vec<i32> {
lista.into_iter().map(|v| v.into_iter()).flatten().collect()
}
// CORRETO
fn processar(lista: Vec<Vec<i32>>) -> Vec<i32> {
lista.into_iter().flat_map(|v| v.into_iter()).collect()
}
12. clippy::expect_fun_call — Formatação em expect
let nome = "arquivo.txt";
// AVISO: alocação desnecessária em expect
let conteudo = std::fs::read_to_string(nome)
.expect(&format!("Falha ao ler {}", nome));
// CORRETO: use unwrap_or_else para lazy evaluation
let conteudo = std::fs::read_to_string(nome)
.unwrap_or_else(|_| panic!("Falha ao ler {}", nome));
Configurando o Clippy
No Código (por Item)
// Permitir um lint específico
#[allow(clippy::needless_return)]
fn funcao() -> i32 {
return 42;
}
// Negar (tratar como erro)
#[deny(clippy::unwrap_used)]
fn funcao_segura() {
// unwrap() aqui causaria erro de compilação
}
No Arquivo clippy.toml
# clippy.toml na raiz do projeto
too-many-arguments-threshold = 10
type-complexity-threshold = 500
cognitive-complexity-threshold = 30
No Cargo.toml ou lib.rs/main.rs
// No topo do arquivo principal
#![warn(clippy::all)]
#![warn(clippy::pedantic)]
#![allow(clippy::module_name_repetitions)]
Níveis de Lints do Clippy
| Categoria | Flag | Descrição |
|---|---|---|
clippy::all | Padrão | Lints mais comuns e consensuais |
clippy::pedantic | Opcional | Lints mais rigorosos |
clippy::nursery | Experimental | Lints em desenvolvimento |
clippy::cargo | Cargo | Problemas no Cargo.toml |
clippy::restriction | Restritivo | Para projetos com regras específicas |
Dica: CI com Clippy
Adicione ao seu CI (GitHub Actions, etc.):
- name: Clippy
run: cargo clippy -- -D warnings
Isso garante que nenhum warning do Clippy entre no código principal.