Clippy: Lints Mais Comuns e Como Resolver

Guia dos lints mais comuns do Clippy no Rust: needless_return, redundant_clone, unused_imports, manual_map e outros. Aprenda a escrever código Rust mais idiomático.

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

CategoriaFlagDescrição
clippy::allPadrãoLints mais comuns e consensuais
clippy::pedanticOpcionalLints mais rigorosos
clippy::nurseryExperimentalLints em desenvolvimento
clippy::cargoCargoProblemas no Cargo.toml
clippy::restrictionRestritivoPara 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.

Veja Também